package jfxtras.icalendarfx.misc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Test; import jfxtras.icalendarfx.VCalendar; import jfxtras.icalendarfx.VChild; import jfxtras.icalendarfx.components.DaylightSavingTime; import jfxtras.icalendarfx.components.StandardTime; import jfxtras.icalendarfx.components.VEvent; import jfxtras.icalendarfx.components.VTimeZone; import jfxtras.icalendarfx.components.VTodo; import jfxtras.icalendarfx.parameters.ValueParameter; import jfxtras.icalendarfx.properties.calendar.CalendarScale; import jfxtras.icalendarfx.properties.calendar.ProductIdentifier; import jfxtras.icalendarfx.properties.calendar.Version; import jfxtras.icalendarfx.properties.component.descriptive.Comment; import jfxtras.icalendarfx.properties.component.recurrence.rrule.FrequencyType; import jfxtras.icalendarfx.properties.component.recurrence.rrule.RecurrenceRuleValue; import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByDay; import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByMonth; import jfxtras.icalendarfx.properties.component.time.DateTimeStart; public class MiscICalendarTests { @Test public void canCreateEmptyVCalendar() { // Create top-level calendar element VCalendar c = new VCalendar(); // Produce text like what is shown in RFC 5545 String expectedContent = "BEGIN:VCALENDAR" + System.lineSeparator() + "END:VCALENDAR"; String content = c.toString(); assertEquals(expectedContent, content); } @Test public void canAddElementsWithSetters() { VCalendar calendar = new VCalendar(); VEvent event = new VEvent(); // Properties can be set a number of ways event.setDateTimeStart(new DateTimeStart(LocalDateTime.of(2007, 11, 4, 20, 0))); // most explicit - reference to new property event.setDateTimeStart(LocalDateTime.of(2007, 11, 4, 20, 0)); // passing value of property object event.setDateTimeStart("DTSTART:20071104T020000"); // by parsing iCalendar content event.setDateTimeStart("20071104T020000"); // parsing iCalendar value (tag omitted) // the last three setters the API builds new DateTimeStart objects for you. calendar.setVEvents(new ArrayList<>(Arrays.asList(event))); // add the event to the calendar calendar.setProductIdentifier("-//jfxtras/iCalendarFx//EN"); // PRODID calendar property String expectedContent = "BEGIN:VCALENDAR" + System.lineSeparator() + "BEGIN:VEVENT" + System.lineSeparator() + "DTSTART:20071104T020000" + System.lineSeparator() + "END:VEVENT" + System.lineSeparator() + "PRODID:-//jfxtras/iCalendarFx//EN" + System.lineSeparator() + "END:VCALENDAR"; assertEquals(expectedContent, calendar.toString()); } @Test public void canAddElementsWithListSetters() { VEvent event = new VEvent(); Comment comment = Comment.parse("A comment"); List<Comment> comments = Arrays.asList(comment); event.setComments(comments); assertEquals(comments, event.getComments()); } @Test // parse a string public void canCreateVCalendarByChaining2() { VEvent event = new VEvent() .withDateTimeStamp(ZonedDateTime.of(LocalDateTime.of(2015, 1, 10, 8, 0), ZoneOffset.UTC)) // DTSTAMP component property .withDateTimeStart(LocalDateTime.of(2015, 11, 3, 10, 0)) // DTSTART component property .withDuration(Duration.ofMinutes(90)) // DURATION component property .withRecurrenceRule(new RecurrenceRuleValue() // RRULE component property .withFrequency(FrequencyType.MONTHLY) // FREQ rrule value element .withByRules(new ByMonth(Month.NOVEMBER, Month.DECEMBER), // BYMONTH rrule value element new ByDay(DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY))) // BYDAY rrule value element .withUniqueIdentifier("20150110T080000-0@jfxtras.org"); // UID component property String content = event.toString(); } @Test public void canCreateVCalendarByChaining() { VCalendar c = new VCalendar() .withProductIdentifier(new ProductIdentifier("-//jfxtras/iCalendarFx//EN")) // PRODID calendar property .withVersion(new Version()) // VERSION calendar property .withCalendarScale(new CalendarScale()) // CALSCALE calendar property .withVTodos(new VTodo() // VTODO calendar component .withDateTimeCompleted("COMPLETED:19960401T150000Z") // CREATED component property .withDateTimeDue("TZID=America/Los_Angeles:19960401T050000") // DUE component property .withPercentComplete(35)) // PERCENT-COMPLETE component property .withVTimeZones(new VTimeZone() // VTIMEZONE calendar component .withTimeZoneIdentifier("America/New_York") // TZID component property .withDateTimeLastModified("20050809T050000Z") // LAST-MODIFIED component property .withStandardOrDaylight( new DaylightSavingTime() // 1 - DAYLIGHT component sub-component .withDateTimeStart("19670430T020000") // DTSTART component property .withRecurrenceRule("FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z") // RRULE component property .withTimeZoneOffsetFrom("-0500") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0400") // TZOFFSETTO component property .withTimeZoneNames("EDT"), // TZNAME component property new StandardTime() // 2 - STANDARD component sub-component .withDateTimeStart("19671029T020000") // DTSTART component property .withRecurrenceRule("RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z") // RRULE component property .withTimeZoneOffsetFrom("-0400") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0500") // TZOFFSETTO component property .withTimeZoneNames("EST"), // TZNAME component property new DaylightSavingTime() // 3 - DAYLIGHT component sub-component .withDateTimeStart("19740106T020000") // DTSTART component property .withRecurrenceDates("RDATE:19750223T020000") // RRULE component property .withTimeZoneOffsetFrom("-0500") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0400") // TZOFFSETTO component property .withTimeZoneNames("EDT"), // TZNAME component property new DaylightSavingTime() // 4 - DAYLIGHT component sub-component .withDateTimeStart("19760425T020000") // DTSTART component property .withRecurrenceRule("RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z") // RRULE component property .withTimeZoneOffsetFrom("-0500") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0400") // TZOFFSETTO component property .withTimeZoneNames("EDT"), // TZNAME component property new DaylightSavingTime() // 5 - DAYLIGHT component sub-component .withDateTimeStart("19870405T020000") // DTSTART component property .withRecurrenceRule("RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z") // RRULE component property .withTimeZoneOffsetFrom("-0500") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0400") // TZOFFSETTO component property .withTimeZoneNames("EDT"), // TZNAME component property new DaylightSavingTime() // 6 - DAYLIGHT component sub-component .withDateTimeStart("20070311T020000") // DTSTART component property .withRecurrenceRule("RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU") // RRULE component property .withTimeZoneOffsetFrom("-0500") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0400") // TZOFFSETTO component property .withTimeZoneNames("EDT"), // TZNAME component property new StandardTime() // 7 - STANDARD component sub-component .withDateTimeStart("20071104T020000") // DTSTART component property .withRecurrenceRule("RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU") // RRULE component property .withTimeZoneOffsetFrom("-0400") // TZOFFSETFROM component property .withTimeZoneOffsetTo("-0500") // TZOFFSETTO component property .withTimeZoneNames("EST"))) // TZNAME component property .withVEvents( new VEvent() .withCategories("group13") // CATEGORIES component property .withDateTimeCreated(ZonedDateTime.of(LocalDateTime.of(2015, 11, 9, 8, 29), ZoneOffset.UTC)) // CREATED component property .withDescription("Yearly1 Description") // DESCRIPTION component property .withDateTimeStamp(ZonedDateTime.of(LocalDateTime.of(2015, 11, 9, 8, 30), ZoneOffset.UTC)) // DTSTAMP component property .withDateTimeStart(LocalDateTime.of(2015, 11, 9, 10, 0)) // DTSTART component property .withDuration(Duration.ofHours(1)) // DURATION component property .withDateTimeLastModified(ZonedDateTime.of(LocalDateTime.of(2015, 11, 10, 18, 30), ZoneOffset.UTC)) // LAST-MODIFIED component property .withRecurrenceRule(new RecurrenceRuleValue() // RRULE component property .withFrequency(FrequencyType.YEARLY)) // FREQ rrule value element .withSummary("Yearly1 Summary") // SUMMARY component property .withUniqueIdentifier("20151109T082900-0@jfxtras.org"), // UID component property new VEvent() .withDateTimeStamp(ZonedDateTime.of(LocalDateTime.of(2015, 1, 10, 8, 0), ZoneOffset.UTC)) // DTSTAMP component property .withDateTimeStart(LocalDateTime.of(2015, 11, 3, 10, 0)) // DTSTART component property .withDuration(Duration.ofMinutes(90)) // DURATION component property .withRecurrenceRule(new RecurrenceRuleValue() // RRULE component property .withFrequency(FrequencyType.MONTHLY) // FREQ rrule value element .withByRules(new ByMonth(Month.NOVEMBER, Month.DECEMBER), // BYMONTH rrule value element new ByDay(DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY))) // BYDAY rrule value element .withUniqueIdentifier("20150110T080000-0@jfxtras.org")); // UID component property } @Test // parse a string public void canCreateVCalendarByParsing() { String nl = System.lineSeparator(); String content = "BEGIN:VCALENDAR" + nl + "VERSION:2.0" + nl + "PRODID:-//jfxtras/iCalendarFx//EN" + nl + "BEGIN:VEVENT" + nl + "UID:19970610T172345Z@jfxtras.org" + nl + "DTSTAMP:19970610T172345Z" + nl + "DTSTART:19970714T170000Z" + nl + "DTEND:19970715T040000Z" + nl + "SUMMARY:Bastille Day Party" + nl + "END:VEVENT" + nl + "END:VCALENDAR"; VCalendar c = VCalendar.parse(content); // Note: can also parse files and readers } @Test // Can check if RFC 5545 rules are followed. public void canValidate() { VEvent vEvent = new VEvent() .withDateTimeStart(LocalDate.of(2016, 3, 7)) .withDateTimeEnd(LocalDate.of(2016, 3, 8)) .withDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z"))) .withUniqueIdentifier(); assertTrue(vEvent.isValid()); } @Test // Can check if RFC 5545 rules are followed. public void canValidate2() { VEvent e = new VEvent(); assertEquals(3, e.errors().size()); assertFalse(e.isValid()); } // iCalendar Transport-Independent Interoperability Protocol (iTIP) message defined in RFC 5546 // Note: by default only supports PUBLISH, REQUEST and CANCEL messages without any ATTENDEES // additional features can be added by replacing the default iTIP factory and providing your own iTIP process classes. @Test public void canProcessPublishiTIPMessage() { String ls = System.lineSeparator(); VCalendar main = new VCalendar(); String publish = "BEGIN:VCALENDAR"+ls+ "METHOD:PUBLISH"+ls+ "PRODID:-//jfxtras/iCalendarFx//EN"+ls+ "VERSION:2.0"+ls+ "BEGIN:VEVENT"+ls+ "ORGANIZER:mailto:a@example.com"+ls+ "DTSTART:19970701T200000Z"+ls+ "DTSTAMP:19970611T190000Z"+ls+ "SUMMARY:Jim' Birthday Party"+ls+ "UID:1234234-23@example.com"+ls+ "END:VEVENT"+ls+ "END:VCALENDAR"; List<String> log = main.processITIPMessage(publish); List<String> expectedLog = Arrays.asList("SUCCESS: added VEvent with UID:1234234-23@example.com"); assertEquals(expectedLog, log); } @Test public void canProcessCanceliTIPMessage() { String mainContent = "BEGIN:VCALENDAR" + System.lineSeparator() + "VERSION:2.0" + System.lineSeparator() + "PRODID:-//Example/ExampleCalendarClient//EN" + System.lineSeparator() + "BEGIN:VEVENT" + System.lineSeparator() + "ORGANIZER:mailto:a@example.com" + System.lineSeparator() + "DTSTART:19970705T200000Z" + System.lineSeparator() + "DTSTAMP:19970611T190000Z" + System.lineSeparator() + "SUMMARY:ST. PAUL SAINTS -VS- DULUTH-SUPERIOR DUKES" + System.lineSeparator() + "UID:0981234-1234234-23@example.com" + System.lineSeparator() + "END:VEVENT" + System.lineSeparator() + "END:VCALENDAR"; VCalendar main = VCalendar.parse(mainContent); String cancel = "BEGIN:VCALENDAR" + System.lineSeparator() + "METHOD:CANCEL" + System.lineSeparator() + "PRODID:-//Example/ExampleCalendarClient//EN" + System.lineSeparator() + "VERSION:2.0" + System.lineSeparator() + "BEGIN:VEVENT" + System.lineSeparator() + "ORGANIZER:mailto:a@example.com" + System.lineSeparator() + "COMMENT:DUKES forfeit the game" + System.lineSeparator() + "SEQUENCE:2" + System.lineSeparator() + "UID:0981234-1234234-23@example.com" + System.lineSeparator() + "DTSTAMP:19970611T190000Z" + System.lineSeparator() + "END:VEVENT" + System.lineSeparator() + "END:VCALENDAR"; List<String> log = main.processITIPMessage(cancel); } @Test // parent-child hierarchy (e.g. find properties in a VEvent) public void canCreateChildrenList() { String ls = System.lineSeparator(); String content = "BEGIN:VEVENT"+ls+ "UID:AF23B2@example.com"+ls+ "DTSTAMP:19970610T172345Z"+ls+ "DTSTART;VALUE=DATE:19970714"+ls+ "SUMMARY:Bastille Day Party"+ls+ "END:VEVENT"; VEvent e = VEvent.parse(content); List<VChild> eChildren = e.childrenUnmodifiable(); DateTimeStart d = e.getDateTimeStart(); assertTrue(eChildren.contains(d)); List<VChild> dChildren = d.childrenUnmodifiable(); ValueParameter dt = d.getValueType(); assertTrue(dChildren.contains(dt)); } }