4 * Created on 26 November 2006, 15:44
5 * Copyright (C) Mathew McBride 2006-2007
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
;
58 * Provides utilities for converting ical4j data objects to Fumambol
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
{
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();
96 lmprop.setPropertyValue(lm.getValue());
97 fnblcontent.setLastModified(lmprop);
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());
105 /* Start off with basic attributes.. DTSTART, DTEND, SUMMARY, DESCRIPTION, LOCATION,
107 if (comp
.getProperty(DtStart
.DTSTART
) != null) {
108 dts
= (DtStart
) comp
.getProperty(DtStart
.DTSTART
);
109 Parameter v
= dts
.getParameter("VALUE");
111 v
.getValue().equals("DATE")) {
112 String newValue
= dts
.getValue();
113 attachAllDayDtStart(fnblcontent
, newValue
, clientType
);
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");
127 v
.getValue().equals("DATE")) {
128 String newValue
= dte
.getValue();
129 attachAllDayDtEnd(fnblcontent
, newValue
, clientType
);
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");
143 v
.getValue().equals("DATE")) {
144 String newValue
= due
.getValue();
145 attachAllDayDtEnd(fnblcontent
, newValue
, clientType
);
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
);
161 net
.fortuna
.ical4j
.model
.Date dt
= new net
.fortuna
.ical4j
.model
.Date(dte
.getDate().getTime());
163 net
.fortuna
.ical4j
.model
.Date ds
= new net
.fortuna
.ical4j
.model
.Date(dts
.getDate().getTime());
165 attachAllDayDtStart(fnblcontent
, dts
.getValue(), clientType
);
166 attachAllDayDtEnd(fnblcontent
, dte
.getValue(), clientType
);
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;
248 if (r
.getUntil() == null) {
251 net
.fortuna
.ical4j
.model
.Date eDate
= r
.getUntil();
252 endDate
= eDate
.toString();
254 short dayOfWeekMask
= 0;
255 if (freq
.equals(Recur
.DAILY
)) {
257 RecurrencePattern rp
= RecurrencePattern
.getDailyRecurrencePattern(count
,
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
)) {
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")) {
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
,
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")) {
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(
331 weekoffset
.shortValue(),
336 fnblcontent
.setRecurrencePattern(rp
);
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
,
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")) {
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(),
381 weekoffset
.shortValue(),
387 if (r
.getInterval() < 0) {
390 Integer dayOfMonth
= (Integer
) r
.getMonthDayList().get(0);
391 Integer monthOfYear
= (Integer
) r
.getMonthList().get(0);
392 RecurrencePattern rp
= RecurrencePattern
.getYearlyRecurrencePattern(
394 dayOfMonth
.shortValue(),
395 monthOfYear
.shortValue(),
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
);
411 /* NOT YET CONVERTED: attach / attendee / exdate / exrule / geo / seq
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
);
421 e
.getDtEnd().setPropertyValue(newValue
);
422 } else if (clientType
.equals("text/x-s4j-sife")) {
423 e
.getDtEnd().setPropertyValue(newValue
);
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
);
434 e
.getDtStart().setPropertyValue(newValue
);
435 } else if (clientType
.equals("text/x-s4j-sife")) {
436 e
.getDtStart().setPropertyValue(newValue
);
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();
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
);
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();
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) {
496 e
.setDAlarm(new com
.funambol
.common
.pim
.common
.Property(buf
.toString()));