Skip to content

Commit 723c39f

Browse files
committed
Remove EventValidator and integrate its functionality into RecurrenceFieldsProcessor.
1 parent 7cbc9d4 commit 723c39f

File tree

7 files changed

+161
-252
lines changed

7 files changed

+161
-252
lines changed

lib/src/main/kotlin/at/bitfire/ical4android/EventReader.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package at.bitfire.ical4android
88

99
import at.bitfire.ical4android.ICalendar.Companion.CALENDAR_NAME
1010
import at.bitfire.ical4android.ICalendar.Companion.fromReader
11-
import at.bitfire.ical4android.validation.EventValidator
1211
import at.bitfire.synctools.icalendar.CalendarUidSplitter
1312
import at.bitfire.synctools.icalendar.Css3Color
1413
import net.fortuna.ical4j.data.ParserException
@@ -52,8 +51,7 @@ class EventReader {
5251
get() = Logger.getLogger(javaClass.name)
5352

5453
/**
55-
* Parses an iCalendar resource, applies [at.bitfire.synctools.icalendar.validation.ICalPreprocessor]
56-
* and [EventValidator] to increase compatibility and extracts the VEVENTs.
54+
* Parses an iCalendar resource and applies [at.bitfire.synctools.icalendar.validation.ICalPreprocessor].
5755
*
5856
* @param from where the iCalendar is read from
5957
* @param properties Known iCalendar properties (like [CALENDAR_NAME]) will be put into this map. Key: property name; value: property value
@@ -105,10 +103,6 @@ class EventReader {
105103
events += event
106104
}
107105

108-
// Try to repair all events after reading the whole iCalendar
109-
for (event in events)
110-
EventValidator.repair(event)
111-
112106
return events
113107
}
114108

lib/src/main/kotlin/at/bitfire/ical4android/EventWriter.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import at.bitfire.ical4android.ICalendar.Companion.minifyVTimeZone
1010
import at.bitfire.ical4android.ICalendar.Companion.softValidate
1111
import at.bitfire.ical4android.ICalendar.Companion.withUserAgents
1212
import at.bitfire.ical4android.util.DateUtils.isDateTime
13-
import at.bitfire.ical4android.validation.EventValidator
1413
import at.bitfire.synctools.exception.InvalidLocalResourceException
1514
import net.fortuna.ical4j.data.CalendarOutputter
1615
import net.fortuna.ical4j.model.Calendar
@@ -46,7 +45,7 @@ class EventWriter(
4645

4746

4847
/**
49-
* Applies error correction over [EventValidator] to an [Event] and generates an iCalendar from it.
48+
* Generates an iCalendar from the given [Event].
5049
*
5150
* @param event event to generate iCalendar from
5251
* @param to stream that the iCalendar is written to
@@ -58,8 +57,6 @@ class EventWriter(
5857

5958
val dtStart = event.dtStart ?: throw InvalidLocalResourceException("Won't generate event without start time")
6059

61-
EventValidator.repair(event) // repair this event before creating the VEVENT
62-
6360
// "main event" (without exceptions)
6461
val components = ical.components
6562
val mainEvent = toVEvent(event)

lib/src/main/kotlin/at/bitfire/ical4android/validation/EventValidator.kt

Lines changed: 0 additions & 169 deletions
This file was deleted.

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/EndTimeBuilder.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class EndTimeBuilder: AndroidEntityBuilder {
8686
* - DTEND and DTSTART are both either DATE or DATE-TIME → original DTEND
8787
* - DTEND is DATE, DTSTART is DATE-TIME → DTEND is amended to DATE-TIME with time and timezone from DTSTART
8888
* - DTEND is DATE-TIME, DTSTART is DATE → DTEND is reduced to its date component
89+
*
90+
* @see at.bitfire.synctools.mapping.calendar.processor.RecurrenceFieldsProcessor.alignUntil
8991
*/
9092
@VisibleForTesting
9193
internal fun alignWithDtStart(dtEnd: DtEnd, dtStart: DtStart): DtEnd {

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/processor/RecurrenceFieldsProcessor.kt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,21 @@ package at.bitfire.synctools.mapping.calendar.processor
99
import android.content.Entity
1010
import android.provider.CalendarContract.Events
1111
import at.bitfire.ical4android.Event
12+
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDate
13+
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDateTime
14+
import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate
15+
import at.bitfire.ical4android.util.TimeApiExtensions.toZonedDateTime
1216
import at.bitfire.synctools.exception.InvalidLocalResourceException
1317
import at.bitfire.synctools.util.AndroidTimeUtils
18+
import net.fortuna.ical4j.model.Date
19+
import net.fortuna.ical4j.model.DateTime
20+
import net.fortuna.ical4j.model.Recur
1421
import net.fortuna.ical4j.model.TimeZoneRegistry
1522
import net.fortuna.ical4j.model.property.ExDate
1623
import net.fortuna.ical4j.model.property.ExRule
1724
import net.fortuna.ical4j.model.property.RDate
1825
import net.fortuna.ical4j.model.property.RRule
26+
import java.time.ZonedDateTime
1927
import java.util.LinkedList
2028
import java.util.logging.Level
2129
import java.util.logging.Logger
@@ -33,13 +41,26 @@ class RecurrenceFieldsProcessor(
3341
val tsStart = values.getAsLong(Events.DTSTART) ?: throw InvalidLocalResourceException("Found event without DTSTART")
3442
val allDay = (values.getAsInteger(Events.ALL_DAY) ?: 0) != 0
3543

44+
// provide start date as ical4j Date, if needed
45+
val startDate by lazy {
46+
AndroidTimeField(
47+
timestamp = tsStart,
48+
timeZone = values.getAsString(Events.EVENT_TIMEZONE),
49+
allDay = allDay,
50+
tzRegistry = tzRegistry
51+
).asIcal4jDate()
52+
}
53+
3654
// process RRULE field
3755
val rRules = LinkedList<RRule>()
3856
values.getAsString(Events.RRULE)?.let { rRuleField ->
3957
try {
4058
for (rule in rRuleField.split(AndroidTimeUtils.RECURRENCE_RULE_SEPARATOR)) {
4159
val rule = RRule(rule)
4260

61+
// align RRULE UNTIL to DTSTART, if needed
62+
rule.recur = alignUntil(rule.recur, startDate)
63+
4364
// skip if UNTIL is before event's DTSTART
4465
val tsUntil = rule.recur.until?.time
4566
if (tsUntil != null && tsUntil <= tsStart) {
@@ -74,6 +95,9 @@ class RecurrenceFieldsProcessor(
7495
for (rule in exRuleField.split(AndroidTimeUtils.RECURRENCE_RULE_SEPARATOR)) {
7596
val rule = ExRule(null, rule)
7697

98+
// align RRULE UNTIL to DTSTART, if needed
99+
rule.recur = alignUntil(rule.recur, startDate)
100+
77101
// skip if UNTIL is before event's DTSTART
78102
val tsUntil = rule.recur.until?.time
79103
if (tsUntil != null && tsUntil <= tsStart) {
@@ -109,4 +133,53 @@ class RecurrenceFieldsProcessor(
109133
}
110134
}
111135

136+
/**
137+
* Aligns the `UNTIL` of the given recurrence info to the VALUE-type (DATE-TIME/DATE) of [startDate].
138+
*
139+
* @param recur recurrence info whose `UNTIL` shall be aligned
140+
* @param startDate `DTSTART` date to compare with
141+
*
142+
* @return
143+
*
144+
* - UNTIL not set → original recur
145+
* - UNTIL and DTSTART are both either DATE or DATE-TIME → original recur
146+
* - UNTIL is DATE, DTSTART is DATE-TIME → UNTIL is amended to DATE-TIME with time and timezone from DTSTART
147+
* - UNTIL is DATE-TIME, DTSTART is DATE → UNTIL is reduced to its date component
148+
*
149+
* @see at.bitfire.synctools.mapping.calendar.builder.EndTimeBuilder.alignWithDtStart
150+
*/
151+
fun alignUntil(recur: Recur, startDate: Date): Recur {
152+
val until: Date? = recur.until
153+
if (until == null)
154+
return recur
155+
156+
if (until is DateTime) {
157+
// UNTIL is DATE-TIME
158+
if (startDate is DateTime) {
159+
// DTSTART is DATE-TIME
160+
return recur
161+
} else {
162+
// DTSTART is DATE → only take date part
163+
val untilDate = until.toLocalDate()
164+
return Recur.Builder(recur)
165+
.until(untilDate.toIcal4jDate())
166+
.build()
167+
}
168+
} else {
169+
// UNTIL is DATE
170+
if (startDate is DateTime) {
171+
// DTSTART is DATE-TIME
172+
val untilDate = until.toLocalDate()
173+
val startTime = startDate.toZonedDateTime()
174+
val untilDateWithTime = ZonedDateTime.of(untilDate, startTime.toLocalTime(), startTime.zone)
175+
return Recur.Builder(recur)
176+
.until(untilDateWithTime.toIcal4jDateTime())
177+
.build()
178+
} else {
179+
// DTSTART is DATE
180+
return recur
181+
}
182+
}
183+
}
184+
112185
}

0 commit comments

Comments
 (0)