Skip to content

Commit ba1052c

Browse files
committed
Add KDoc
1 parent 8d9c6c3 commit ba1052c

File tree

6 files changed

+36
-21
lines changed

6 files changed

+36
-21
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ class AndroidEvent(
786786
private fun buildEvent(recurrence: Event?, builder: CpoBuilder) {
787787
val event = recurrence ?: requireNotNull(event)
788788

789-
val dtStart = event.dtStart ?: throw LocalStorageException("Events must have DTSTART")
789+
val dtStart = event.dtStart ?: throw InvalidLocalResourceException("Events must have DTSTART")
790790
val allDay = DateUtils.isDate(dtStart)
791791

792792
// make sure that time zone is supported by Android

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ data class Event(
253253
* @throws IOException on I/O errors
254254
* @throws ParserException when the iCalendar can't be parsed
255255
*/
256+
@Deprecated("Use ICalendarParser and CalendarUidSplitter directly")
256257
fun eventsFromReader(
257258
reader: Reader,
258259
properties: MutableMap<String, String>? = null
@@ -277,23 +278,20 @@ data class Event(
277278

278279
// convert into Events (data class)
279280
val events = mutableListOf<Event>()
280-
for ((uid, associatedEvents) in vEventsByUid) {
281-
val mainVEvent =
282-
if (associatedEvents.main != null)
283-
associatedEvents.main
284-
else {
285-
logger.info("UID $uid doesn't have a main event but only exceptions; creating fake main event")
286-
associatedEvents.exceptions.first()
287-
}
281+
for (associatedEvents in vEventsByUid.values) {
282+
val mainVEvent = associatedEvents.main ?:
283+
// no main event but only exceptions, create fake main event
284+
// FIXME: we should construct a valid recurring fake event, not just take the exception
285+
associatedEvents.exceptions.first()
288286

289287
val event = fromVEvent(mainVEvent)
290-
event.exceptions.addAll(associatedEvents.exceptions.map {
291-
fromVEvent(it).also { exception ->
288+
for (exceptionVEvent in associatedEvents.exceptions) {
289+
event.exceptions += fromVEvent(exceptionVEvent).also { exception ->
292290
// make sure that exceptions have at least a SUMMARY (if the main event does have one)
293291
if (exception.summary == null)
294292
exception.summary = event.summary
295293
}
296-
})
294+
}
297295

298296
events += event
299297
}

lib/src/main/kotlin/at/bitfire/synctools/icalendar/AssociatedComponents.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ import net.fortuna.ical4j.model.component.CalendarComponent
1010
import net.fortuna.ical4j.model.component.VEvent
1111

1212
/**
13+
* Represents a set of components (like VEVENT) stored in a calendar object resource as defined
14+
* in RFC 4791 section 4.1. It consists of
15+
*
16+
* - an (optional) main component,
17+
* - optional exceptions of this main component.
18+
*
19+
* Note: It's possible and valid that there's no main component, but only exceptions, for instance
20+
* when the user has been invited to a specific instance (= exception) of a recurring event, but
21+
* not to the event as a whole (→ main event is unknown / not present).
22+
*
1323
* @param main main component (with or without UID, but without RECURRENCE-ID), may be `null` if only exceptions are present
1424
* @param exceptions exceptions (each without RECURRENCE-ID); UID must be
1525
* 1. the same as the UID of [main],
@@ -34,6 +44,9 @@ data class AssociatedComponents<T: CalendarComponent>(
3444
* @throws IllegalArgumentException if [main] and/or [exceptions] UIDs don't match
3545
*/
3646
private fun validate() {
47+
if (main == null && exceptions.isEmpty())
48+
throw IllegalArgumentException("At least one component is required")
49+
3750
val mainUid =
3851
if (main != null) {
3952
if (main.recurrenceId != null)

lib/src/main/kotlin/at/bitfire/synctools/icalendar/CalendarUidSplitter.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,22 @@ import androidx.annotation.VisibleForTesting
1010
import net.fortuna.ical4j.model.Calendar
1111
import net.fortuna.ical4j.model.component.CalendarComponent
1212

13-
/**
14-
* Splits iCalendar components by UID.
15-
*/
1613
class CalendarUidSplitter<T: CalendarComponent> {
1714

15+
/**
16+
* Splits iCalendar components by UID and classifies them as main events (without RECURRENCE-ID)
17+
* or exceptions (with RECURRENCE-ID).
18+
*
19+
* When there are multiple components with the same UID and RECURRENCE-ID, but different SEQUENCE,
20+
* this method keeps only the ones with the highest SEQUENCE.
21+
*/
1822
fun associateByUid(calendar: Calendar, componentName: String): Map<String?, AssociatedComponents<T>> {
1923
// get all components of type T (for instance: all VEVENTs)
20-
val allComponents = calendar.getComponents<T>(componentName).toMutableList()
24+
val all = calendar.getComponents<T>(componentName)
2125

2226
// Note for VEVENT: UID is REQUIRED in RFC 5545 section 3.6.1, but optional in RFC 2445 section 4.6.1,
2327
// so it's possible that the Uid is null.
24-
val byUid: Map<String?, List<T>> = allComponents
28+
val byUid: Map<String?, List<T>> = all
2529
.groupBy { it.uid?.value }
2630
.mapValues { filterBySequence(it.value) }
2731

@@ -38,9 +42,9 @@ class CalendarUidSplitter<T: CalendarComponent> {
3842
/**
3943
* Keeps only the events with the highest SEQUENCE (per RECURRENCE-ID).
4044
*
41-
* @param events list of VEVENTs with the same UID, but different RECURRENCE-IDs and SEQUENCEs
45+
* @param events list of VEVENTs with the same UID, but different RECURRENCE-IDs (may be `null`) and SEQUENCEs
4246
*
43-
* @return same as input list, but each RECURRENCE-ID event only with the highest SEQUENCE
47+
* @return same as input list, but each RECURRENCE-ID occurs only with the highest SEQUENCE
4448
*/
4549
@VisibleForTesting
4650
internal fun filterBySequence(events: List<T>): List<T> {

lib/src/main/kotlin/at/bitfire/synctools/icalendar/ICalendarParser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class ICalendarParser {
2727
get() = Logger.getLogger(javaClass.name)
2828

2929
/**
30-
* Parses the given iCalendar and applies some error correction:
30+
* Parses the given iCalendar as lenient as possible and applies some error correction:
3131
*
3232
* 1. The input stream from is preprocessed with [ICalPreprocessor.preprocessStream].
3333
* 2. The parsed calendar is preprocessed with [ICalPreprocessor.preprocessCalendar].

lib/src/test/kotlin/at/bitfire/synctools/icalendar/AssociatedComponentsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import org.junit.Test
1414

1515
class AssociatedComponentsTest {
1616

17-
@Test
17+
@Test(expected = IllegalArgumentException::class)
1818
fun testEmpty() {
1919
AssociatedEvents(null, emptyList())
2020
}

0 commit comments

Comments
 (0)