Alarm conversion support option added. Only tested with Synthesis and
[funambol-groupdav-connector.git] / src / net / bionicmessage / funambol / datakit / ical4j2funambol.java
bloba19224578eed44a30b43195d5d1a6b3244cc0918
1 /*
2 * ical4j2funambol.java
4 * Created on 26 November 2006, 15:44
5 * Copyright (C) Mathew McBride 2006-2007
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 package net.bionicmessage.funambol.datakit;
22 import com.funambol.common.pim.calendar.RecurrencePattern;
23 import com.funambol.common.pim.calendar.RecurrencePatternException;
24 import com.funambol.common.pim.calendar.Task;
25 import net.fortuna.ical4j.model.*;
26 import net.fortuna.ical4j.model.Property.*;
28 import net.fortuna.ical4j.data.*;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import net.fortuna.ical4j.model.component.CalendarComponent;
32 import net.fortuna.ical4j.model.component.VAlarm;
33 import net.fortuna.ical4j.model.component.VEvent;
34 import net.fortuna.ical4j.model.component.VToDo;
35 import net.fortuna.ical4j.model.parameter.AltRep;
36 import net.fortuna.ical4j.model.parameter.Encoding;
37 import net.fortuna.ical4j.model.parameter.Related;
38 import net.fortuna.ical4j.model.property.Action;
39 import net.fortuna.ical4j.model.property.Categories;
40 import net.fortuna.ical4j.model.property.Clazz;
41 import net.fortuna.ical4j.model.property.DateProperty;
42 import net.fortuna.ical4j.model.property.Description;
43 import net.fortuna.ical4j.model.property.DtEnd;
44 import net.fortuna.ical4j.model.property.DtStart;
45 import net.fortuna.ical4j.model.property.Due;
46 import net.fortuna.ical4j.model.property.Duration;
47 import net.fortuna.ical4j.model.property.LastModified;
48 import net.fortuna.ical4j.model.property.Location;
49 import net.fortuna.ical4j.model.property.Priority;
50 import net.fortuna.ical4j.model.property.RDate;
51 import net.fortuna.ical4j.model.property.RRule;
52 import net.fortuna.ical4j.model.property.Summary;
53 import net.fortuna.ical4j.model.property.Transp;
54 import net.fortuna.ical4j.model.property.Trigger;
55 import net.fortuna.ical4j.model.property.Uid;
57 /**
58 * Provides utilities for converting ical4j data objects to Fumambol
59 * PDI objects
60 * @author matt
62 public class ical4j2funambol {
64 /** Creates a new instance of ical4j2funambol */
65 public static com.funambol.common.pim.calendar.Calendar convertIcal4jToFunambolEvent(net.fortuna.ical4j.model.Calendar ical4j, String clientType) throws RecurrencePatternException {
66 long dstime = 0;
67 long detime = 0;
68 /* Setup a funambol event */
69 com.funambol.common.pim.calendar.Calendar fnblcal = new com.funambol.common.pim.calendar.Calendar();
70 com.funambol.common.pim.calendar.CalendarContent fnblcontent = null;
71 Component comp = null;
72 comp = (Component) ical4j.getComponents().get(0);
73 if (comp.getName().equals(VEvent.VEVENT)) {
74 fnblcontent = new com.funambol.common.pim.calendar.Event();
75 fnblcal.setEvent((com.funambol.common.pim.calendar.Event) fnblcontent);
76 } else if (comp.getName().equals(VToDo.VTODO)) {
77 fnblcontent = new com.funambol.common.pim.calendar.Task();
78 fnblcal.setTask((Task) fnblcontent);
82 String startDate = ""; /* If we get DtStart, then cache it for Funambol recur */
83 DtStart dts = null; /* Also cache dtstart */
84 /* Blatantly obvious attributes */
85 if (comp.getProperty(Uid.UID) != null) {
86 Uid uid = (Uid) comp.getProperty(Uid.UID);
87 com.funambol.common.pim.common.Property uidprop =
88 new com.funambol.common.pim.common.Property(uid.getValue());
89 fnblcontent.setUid(uidprop);
91 /* if (comp.getProperty(LastModified.LAST_MODIFIED) != null) {
92 LastModified lm = (LastModified)comp.getProperty(LastModified.LAST_MODIFIED);
93 com.funambol.common.pim.common.Property lmprop =
94 new com.funambol.common.pim.common.Property();
95 lm.setUtc(true);
96 lmprop.setPropertyValue(lm.getValue());
97 fnblcontent.setLastModified(lmprop);
98 } */
99 /* if (comp.getProperty(Clazz.CLASS) != null) {
100 Clazz cl = (Clazz)comp.getProperty(Clazz.CLASS);
101 com.funambol.common.pim.common.Property clprop =
102 new com.funambol.common.pim.common.Property(cl.getValue());
104 } */
105 /* Start off with basic attributes.. DTSTART, DTEND, SUMMARY, DESCRIPTION, LOCATION,
106 PRIORITY */
107 if (comp.getProperty(DtStart.DTSTART) != null) {
108 dts = (DtStart) comp.getProperty(DtStart.DTSTART);
109 Parameter v = dts.getParameter("VALUE");
110 if (v != null &&
111 v.getValue().equals("DATE")) {
112 String newValue = dts.getValue();
113 attachAllDayDtStart(fnblcontent, newValue, clientType);
114 } else {
115 dts.setUtc(true);
116 com.funambol.common.pim.common.Property dtsprop = new com.funambol.common.pim.common.Property(dts.getValue());
117 fnblcontent.setDtStart(dtsprop);
118 dstime = dts.getDate().getTime();
119 fnblcontent.setAllDay(false);
121 startDate = fnblcontent.getDtStart().getPropertyValueAsString();
123 if (comp.getProperty(DtEnd.DTEND) != null) {
124 DtEnd dte = (DtEnd) comp.getProperty(DtEnd.DTEND);
125 Parameter v = dte.getParameter("VALUE");
126 if (v != null &&
127 v.getValue().equals("DATE")) {
128 String newValue = dte.getValue();
129 attachAllDayDtEnd(fnblcontent, newValue, clientType);
130 } else {
131 dte.setUtc(true);
132 com.funambol.common.pim.common.Property dteprop =
133 new com.funambol.common.pim.common.Property(dte.getValue());
134 fnblcontent.setDtEnd(dteprop);
135 detime = dte.getDate().getTime();
136 fnblcontent.setAllDay(false);
139 if (comp.getProperty(Due.DUE) != null && comp.getProperty(DtEnd.DTEND) == null) {
140 Due due = (Due) comp.getProperty(Due.DUE);
141 Parameter v = due.getParameter("VALUE");
142 if (v != null &&
143 v.getValue().equals("DATE")) {
144 String newValue = due.getValue();
145 attachAllDayDtEnd(fnblcontent, newValue, clientType);
146 } else {
147 due.setUtc(true);
148 com.funambol.common.pim.common.Property dteprop =
149 new com.funambol.common.pim.common.Property(due.getValue());
150 fnblcontent.setDtEnd(dteprop);
153 /** See if an all day event hasn't sneaked in via the back door */
154 if (detime - dstime == 86340000 || detime - dstime == 86399000) {
155 /* See the vObject minimum interoperability profile - OMA
156 * (OMA-TS-vObjectOMAProfile-V1_0-20050118-C) for information on how to
157 * correctly establish an all day event */
158 fnblcontent.setAllDay(true);
159 DtEnd dte = (DtEnd) comp.getProperty(DtEnd.DTEND);
160 dte.setUtc(false);
161 net.fortuna.ical4j.model.Date dt = new net.fortuna.ical4j.model.Date(dte.getDate().getTime());
162 dte.setDate(dt);
163 net.fortuna.ical4j.model.Date ds = new net.fortuna.ical4j.model.Date(dts.getDate().getTime());
164 dts.setDate(ds);
165 attachAllDayDtStart(fnblcontent, dts.getValue(), clientType);
166 attachAllDayDtEnd(fnblcontent, dte.getValue(), clientType);
167 } else {
168 fnblcontent.setAllDay(false);
170 if (comp.getProperty(Summary.SUMMARY) != null) {
171 /* We need to consider text encodings for this.
172 * ical2 specifies 8 bit and base64 in spec, but vcal1
173 * also allows quoted-printable (EVIL!!!). */
174 Summary summ = (Summary) comp.getProperty(Summary.SUMMARY);
175 com.funambol.common.pim.common.Property summprop =
176 new com.funambol.common.pim.common.Property();
177 // Check for encodings
178 if (summ.getParameter(Encoding.ENCODING) != null) {
179 Encoding enc = (Encoding) summ.getParameter(Encoding.ENCODING);
180 summprop.setEncoding(enc.getValue());
182 summprop.setPropertyValue(summ.getValue());
183 fnblcontent.setSummary(summprop);
185 if (comp.getProperty(Description.DESCRIPTION) != null) {
186 /* We also need to consider alternative text encoding here */
187 Description des = (Description) comp.getProperty(Description.DESCRIPTION);
188 com.funambol.common.pim.common.Property desprop =
189 new com.funambol.common.pim.common.Property();
190 // Check for encdoings
191 if (des.getParameter(Encoding.ENCODING) != null) {
192 Encoding enc = (Encoding) des.getParameter(Encoding.ENCODING);
193 desprop.setEncoding(enc.getValue());
195 desprop.setPropertyValue(des.getValue());
196 fnblcontent.setDescription(desprop);
198 if (comp.getProperty(Location.LOCATION) != null) {
199 Location loc = (Location) comp.getProperty(Location.LOCATION);
200 com.funambol.common.pim.common.Property locprop =
201 new com.funambol.common.pim.common.Property();
202 // Is our location pointing to a URL?
203 if (loc.getParameter(AltRep.ALTREP) != null) {
204 AltRep url = (AltRep) loc.getParameter(AltRep.ALTREP);
205 locprop.setValue("URL");
206 locprop.setType("VCARD");
208 locprop.setPropertyValue(loc.getValue());
209 fnblcontent.setLocation(locprop);
211 if (comp.getProperty(Priority.PRIORITY) != null) {
212 Priority pri = (Priority) comp.getProperty(Priority.PRIORITY);
213 com.funambol.common.pim.common.Property priprop =
214 new com.funambol.common.pim.common.Property(pri.getValue());
215 fnblcontent.setPriority(priprop);
216 } else if (comp.getName().equals(VToDo.VTODO)) {
217 com.funambol.common.pim.common.Property priprop =
218 new com.funambol.common.pim.common.Property("1");
219 fnblcontent.setPriority(priprop);
221 /* Slightly harder */
222 if (comp.getProperty(Transp.TRANSP) != null && fnblcontent instanceof com.funambol.common.pim.calendar.Event) {
223 Transp tsp = (Transp) comp.getProperty(Transp.TRANSP);
224 com.funambol.common.pim.common.Property tspprop =
225 new com.funambol.common.pim.common.Property();
226 if (tsp.getValue().equals(Transp.OPAQUE)) {
227 // This event WILL show up as busy
228 tspprop.setPropertyValue("0");
229 } else { // What calendar app couldn't chose between OPAQUE and TRANSPARENT?
230 tspprop.setPropertyValue("1");
232 com.funambol.common.pim.calendar.Event e =
233 (com.funambol.common.pim.calendar.Event) fnblcontent;
234 e.setTransp(tspprop);
236 if (comp.getProperty(RRule.RRULE) != null) {
237 RRule rr = (RRule) comp.getProperty(RRule.RRULE);
238 Recur r = rr.getRecur();
239 /* This is where things start getting insane. Funambol wants us to
240 * find out every property of the recurrance rule and then use a custom
241 * constructor for each case! */
242 /* ical2 RULE VIOLATION: VEVENT may contain multiple RRULES... */
243 String freq = r.getFrequency();
244 int interval = r.getInterval();
245 int count = r.getCount();
246 boolean noEndDate = false;
247 String endDate = "";
248 if (r.getUntil() == null) {
249 noEndDate = true;
250 } else {
251 net.fortuna.ical4j.model.Date eDate = r.getUntil();
252 endDate = eDate.toString();
254 short dayOfWeekMask = 0;
255 if (freq.equals(Recur.DAILY)) {
256 try {
257 RecurrencePattern rp = RecurrencePattern.getDailyRecurrencePattern(count,
258 startDate,
259 endDate,
260 noEndDate,
261 count,
262 dayOfWeekMask);
263 fnblcontent.setRecurrencePattern(rp);
264 } catch (RecurrencePatternException re) {
265 re.printStackTrace();
266 // Send all RecurrencePatternExceptions to ground for now
268 } else if (freq.equals(Recur.HOURLY)) {
269 /* NOT SUPPORTED BY VCAL1 AND FUNAMBOL */
270 } else if (freq.equals(Recur.WEEKLY)) {
271 /* Weekly */
272 try {
273 ArrayList wkdaylist = r.getDayList();
274 for (int i = 0; i < wkdaylist.size(); i++) {
275 WeekDay wday = (WeekDay) wkdaylist.get(i);
276 String day = wday.getDay();
277 if (day.equals("SU")) {
278 dayOfWeekMask++;
279 } else if (day.equals("MO")) {
280 dayOfWeekMask = (short) (dayOfWeekMask + 2);
281 } else if (day.equals("TU")) {
282 dayOfWeekMask = (short) (dayOfWeekMask + 4);
283 } else if (day.equals("WE")) {
284 dayOfWeekMask = (short) (dayOfWeekMask + 8);
285 } else if (day.equals("TU")) {
286 dayOfWeekMask = (short) (dayOfWeekMask + 16);
287 } else if (day.equals("FR")) {
288 dayOfWeekMask = (short) (dayOfWeekMask + 32);
289 } else if (day.equals("SA")) {
290 dayOfWeekMask = (short) (dayOfWeekMask + 64);
293 RecurrencePattern rp = RecurrencePattern.getWeeklyRecurrencePattern(count,
294 dayOfWeekMask,
295 startDate,
296 endDate,
297 noEndDate,
298 count);
299 fnblcontent.setRecurrencePattern(rp);
300 } catch (RecurrencePatternException re) {
301 re.printStackTrace();
303 } else if (freq.equals(Recur.MONTHLY)) {
304 /* Monthly - we have two sorts of month recurrance - by number day of month
305 * or day of week of month */
306 // Do we have a set week position
307 if (r.getDayList().size() > 0) {
308 // Get the weekday instance
309 WeekDay wd = (WeekDay) r.getDayList().get(0);
310 String day = wd.getDay();
311 int week = wd.getOffset();
312 Integer weekoffset = new Integer(week);
313 if (day.equals("SU")) {
314 dayOfWeekMask++;
315 } else if (day.equals("MO")) {
316 dayOfWeekMask = (short) (dayOfWeekMask + 2);
317 } else if (day.equals("TU")) {
318 dayOfWeekMask = (short) (dayOfWeekMask + 4);
319 } else if (day.equals("WE")) {
320 dayOfWeekMask = (short) (dayOfWeekMask + 8);
321 } else if (day.equals("TU")) {
322 dayOfWeekMask = (short) (dayOfWeekMask + 16);
323 } else if (day.equals("FR")) {
324 dayOfWeekMask = (short) (dayOfWeekMask + 32);
325 } else if (day.equals("SA")) {
326 dayOfWeekMask = (short) (dayOfWeekMask + 64);
328 RecurrencePattern rp = RecurrencePattern.getMonthNthRecurrencePattern(
329 r.getInterval(),
330 dayOfWeekMask,
331 weekoffset.shortValue(),
332 startDate,
333 endDate,
334 noEndDate,
335 r.getCount());
336 fnblcontent.setRecurrencePattern(rp);
337 } else {
338 try {
339 java.util.Calendar mcal = java.util.Calendar.getInstance();
340 mcal.setTime(dts.getDate());
341 Integer dayOfMonth = new Integer(mcal.get(java.util.Calendar.DAY_OF_MONTH));
342 dayOfWeekMask = dayOfMonth.shortValue();
343 RecurrencePattern rp = RecurrencePattern.getMonthlyRecurrencePattern(interval,
344 dayOfWeekMask,
345 startDate,
346 endDate,
347 noEndDate,
348 count);
349 fnblcontent.setRecurrencePattern(rp);
350 } catch (RecurrencePatternException re) {
351 re.printStackTrace();
355 } else if (freq.equals(Recur.YEARLY)) {
356 // Same as monthly recurrence basically.
357 if (r.getDayList().size() > 0) {
358 WeekDay wd = (WeekDay) r.getDayList().get(0);
359 String day = wd.getDay();
360 int week = wd.getOffset();
361 Integer weekoffset = new Integer(week);
362 Integer month = (Integer) r.getMonthList().get(0);
363 if (day.equals("SU")) {
364 dayOfWeekMask++;
365 } else if (day.equals("MO")) {
366 dayOfWeekMask = (short) (dayOfWeekMask + 2);
367 } else if (day.equals("TU")) {
368 dayOfWeekMask = (short) (dayOfWeekMask + 4);
369 } else if (day.equals("WE")) {
370 dayOfWeekMask = (short) (dayOfWeekMask + 8);
371 } else if (day.equals("TU")) {
372 dayOfWeekMask = (short) (dayOfWeekMask + 16);
373 } else if (day.equals("FR")) {
374 dayOfWeekMask = (short) (dayOfWeekMask + 32);
375 } else if (day.equals("SA")) {
376 dayOfWeekMask = (short) (dayOfWeekMask + 64);
378 RecurrencePattern rp = RecurrencePattern.getYearNthRecurrencePattern(r.getInterval(),
379 dayOfWeekMask,
380 month.shortValue(),
381 weekoffset.shortValue(),
382 startDate,
383 endDate,
384 noEndDate,
385 count);
386 } else {
387 if (r.getInterval() < 0) {
388 r.setInterval(1);
390 Integer dayOfMonth = (Integer) r.getMonthDayList().get(0);
391 Integer monthOfYear = (Integer) r.getMonthList().get(0);
392 RecurrencePattern rp = RecurrencePattern.getYearlyRecurrencePattern(
393 r.getInterval(),
394 dayOfMonth.shortValue(),
395 monthOfYear.shortValue(),
396 startDate,
397 endDate,
398 noEndDate,
399 count);
404 com.funambol.common.pim.common.Property prodid =
405 new com.funambol.common.pim.common.Property("-//BionicMessage Funambol Connector//ical4jFunambolconvert//EN");
406 fnblcal.setProdId(prodid);
407 com.funambol.common.pim.common.Property ver =
408 new com.funambol.common.pim.common.Property("1.0");
409 fnblcal.setVersion(ver);
410 return fnblcal;
411 /* NOT YET CONVERTED: attach / attendee / exdate / exrule / geo / seq
412 * recurid */
415 public static void attachAllDayDtEnd(com.funambol.common.pim.calendar.CalendarContent e, String newValue, String clientType) {
416 if (clientType.equals("text/x-vcalendar")) {
417 newValue = newValue + "T240000";
418 com.funambol.common.pim.common.Property dteprop =
419 new com.funambol.common.pim.common.Property(newValue);
420 e.setAllDay(true);
421 e.getDtEnd().setPropertyValue(newValue);
422 } else if (clientType.equals("text/x-s4j-sife")) {
423 e.getDtEnd().setPropertyValue(newValue);
424 e.setAllDay(true);
428 public static void attachAllDayDtStart(com.funambol.common.pim.calendar.CalendarContent e, String newValue, String clientType) {
429 if (clientType.equals("text/x-vcalendar")) {
430 newValue = newValue + "T000000";
431 com.funambol.common.pim.common.Property dtsprop =
432 new com.funambol.common.pim.common.Property(newValue);
433 e.setAllDay(true);
434 e.getDtStart().setPropertyValue(newValue);
435 } else if (clientType.equals("text/x-s4j-sife")) {
436 e.getDtStart().setPropertyValue(newValue);
437 e.setAllDay(true);
440 /** Process any alarms in the event/todo. Since this functionality is optional,
441 * it is in another function
443 public static void attachAlarm(com.funambol.common.pim.calendar.CalendarContent e,
444 net.fortuna.ical4j.model.component.CalendarComponent ical4je) {
445 ComponentList alarms = null;
446 if (ical4je instanceof VEvent) {
447 VEvent ve = (VEvent) ical4je;
448 alarms = ve.getAlarms();
449 } else if (ical4je instanceof VToDo) {
450 VToDo vt = (VToDo) ical4je;
451 alarms = vt.getAlarms();
452 } else {
453 return;
455 Iterator alarmIterator = alarms.iterator();
456 while (alarmIterator.hasNext()) {
457 VAlarm va = (VAlarm) alarmIterator.next();
459 Trigger t = va.getTrigger();
460 String[] dalarmComponents = {"", "", "", ""};
461 if (va.getAction().getValue().equals("DISPLAY")) {
462 if (t.getDateTime() != null) {
463 dalarmComponents[0] = t.getDateTime().toString();
464 } else if (t.getDuration() != null) {
465 Dur dur = t.getDuration();
466 DateProperty relatedTo = null;
467 if (t.getParameter(Related.RELATED) != null &&
468 t.getParameter(Related.RELATED).equals(Related.END)) {
469 relatedTo = (DateProperty) ical4je.getProperty(DtEnd.DTEND);
470 } else {
471 relatedTo = (DateProperty) ical4je.getProperty(DtStart.DTSTART);
473 Date dt = relatedTo.getDate();
474 // Calculate a new DateTime relative to dt
475 java.util.Calendar cal = java.util.Calendar.getInstance();
476 cal.setTime(dt);
477 cal.add(java.util.Calendar.SECOND, -(dur.getSeconds()));
478 cal.add(java.util.Calendar.MINUTE, -(dur.getMinutes()));
479 cal.add(java.util.Calendar.HOUR, -(dur.getHours()));
480 cal.add(java.util.Calendar.DAY_OF_YEAR, -(dur.getDays()));
481 cal.add(java.util.Calendar.WEEK_OF_YEAR, -(dur.getWeeks()));
482 DateTime newDate = new DateTime(cal.getTime());
483 newDate.setUtc(true);
484 dalarmComponents[0] = newDate.toString();
487 StringBuffer buf = new StringBuffer();
488 for (int i = 0; i < dalarmComponents.length; i++) {
489 if (dalarmComponents[i] != null) {
490 buf.append(dalarmComponents[i]);
492 if (dalarmComponents.length != i - 1) {
493 buf.append(";");
496 e.setDAlarm(new com.funambol.common.pim.common.Property(buf.toString()));