Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e6cca076 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Unify VEVENT/VTODO parsing

parent 9459f80d
Loading
Loading
Loading
Loading
+12 −33
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@

package at.bitfire.ical4android

import net.fortuna.ical4j.data.CalendarBuilder
import at.bitfire.ical4android.ICalendar.Companion.CALENDAR_NAME
import net.fortuna.ical4j.data.CalendarOutputter
import net.fortuna.ical4j.data.ParserException
import net.fortuna.ical4j.model.*
@@ -21,7 +21,6 @@ import java.io.IOException
import java.io.OutputStream
import java.io.Reader
import java.util.*
import java.util.logging.Level

class Event: ICalendar() {

@@ -59,42 +58,22 @@ class Event: ICalendar() {
    val unknownProperties = LinkedList<Property>()

    companion object {
        const val CALENDAR_NAME = "X-WR-CALNAME"

        /**
         * Parses an InputStream that contains iCalendar VEVENTs.
         * Parses an iCalendar resource, applies [ICalPreprocessor] to increase compatibility
         * and extracts the VEVENTs.
         *
         * @param reader where the iCalendar is taken from
         * @param properties Known iCalendar properties (like [CALENDAR_NAME]) will be put into this map. Key: property name; value: property value
         *
         * @return array of filled [Event] data objects (may have size 0)
         *
         * @param reader        reader for the input stream containing the VEVENTs (pay attention to the charset)
         * @param properties    map of properties, will be filled with CALENDAR_* values, if applicable (may be null)
         * @return              array of filled Event data objects (may have size 0) – doesn't return null
         * @throws ParserException when the iCalendar can't be parsed
         * @throws IllegalArgumentException when the iCalendar resource contains an invalid value
         * @throws IOException on I/O errors
         * @throws InvalidCalendarException on parsing exceptions
         */
        fun fromReader(reader: Reader, properties: MutableMap<String, String>? = null): List<Event> {
            Constants.log.fine("Parsing iCalendar stream")

            // parse stream
            val ical: Calendar
            try {
                ical = CalendarBuilder().build(reader)
            } catch(e: ParserException) {
                throw InvalidCalendarException("Couldn't parse iCalendar object", e)
            } catch(e: IllegalArgumentException) {
                throw InvalidCalendarException("iCalendar object contains invalid value", e)
            }

            try {
                ICalPreprocessor.preProcess(ical)
            } catch (e: Exception) {
                Constants.log.log(Level.WARNING, "Couldn't pre-process iCalendar", e)
            }

            // fill calendar properties
            properties?.let {
                ical.getProperty(CALENDAR_NAME)?.let { calName ->
                    properties[CALENDAR_NAME] = calName.value
                }
            }
        fun eventsFromReader(reader: Reader, properties: MutableMap<String, String>? = null): List<Event> {
            val ical = fromReader(reader, properties)

            // process VEVENTs
            val vEvents = ical.getComponents<VEvent>(Component.VEVENT)
+53 −0
Original line number Diff line number Diff line
@@ -10,12 +10,14 @@ package at.bitfire.ical4android

import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.data.ParserException
import net.fortuna.ical4j.model.Calendar
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.component.*
import net.fortuna.ical4j.model.property.DateProperty
import net.fortuna.ical4j.model.property.ProdId
import net.fortuna.ical4j.model.property.TzUrl
import java.io.Reader
import java.io.StringReader
import java.util.*
import java.util.logging.Level
@@ -27,6 +29,7 @@ open class ICalendar {
    var sequence: Int? = null

    companion object {

        // static ical4j initialization
        init {
            // reduce verbosity of those two loggers
@@ -36,9 +39,59 @@ open class ICalendar {
            Logger.getLogger(net.fortuna.ical4j.model.Recur::class.java.name).level = Level.CONFIG
        }

        // known iCalendar properties
        const val CALENDAR_NAME = "X-WR-CALNAME"

        /**
         * Default PRODID used when generating iCalendars. If you want another value, set it
         * statically before writing the first iCalendar.
         */
        var prodId = ProdId("+//IDN bitfire.at//ical4android")


        // parser

        /**
         * Parses an iCalendar resource and applies [ICalPreprocessor] to increase compatibility.
         *
         * @param reader where the iCalendar is taken from
         * @param properties Known iCalendar properties (like [CALENDAR_NAME]) will be put into this map. Key: property name; value: property value
         *
         * @return parsed iCalendar resource
         * @throws ParserException when the iCalendar can't be parsed
         * @throws IllegalArgumentException when the iCalendar resource contains an invalid value
         */
        fun fromReader(reader: Reader, properties: MutableMap<String, String>? = null): Calendar {
            Constants.log.fine("Parsing iCalendar stream")

            // parse stream
            val calendar: Calendar
            try {
                calendar = CalendarBuilder().build(reader)
            } catch(e: ParserException) {
                throw InvalidCalendarException("Couldn't parse iCalendar", e)
            } catch(e: IllegalArgumentException) {
                throw InvalidCalendarException("iCalendar contains invalid value", e)
            }

            // apply ICalPreprocessor for increased compatibility
            try {
                ICalPreprocessor.preProcess(calendar)
            } catch (e: Exception) {
                Constants.log.log(Level.WARNING, "Couldn't pre-process iCalendar", e)
            }

            // fill calendar properties
            properties?.let {
                calendar.getProperty(CALENDAR_NAME)?.let { calName ->
                    properties[CALENDAR_NAME] = calName.value
                }
            }

            return calendar
        }


        // time zone helpers

        fun isDateTime(date: DateProperty?) = date != null && date.date is DateTime
+11 −16
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@
package at.bitfire.ical4android

import at.bitfire.ical4android.MiscUtils.TextListHelper.toList
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.data.CalendarOutputter
import net.fortuna.ical4j.data.ParserException
import net.fortuna.ical4j.model.*
@@ -64,23 +63,19 @@ class Task: ICalendar() {
    companion object {

        /**
         * Parses an InputStream that contains iCalendar VTODOs.
         * Parses an iCalendar resource, applies [ICalPreprocessor] to increase compatibility
         * and extracts the VTODOs.
         *
         * @param reader  reader for the input stream containing the VTODOs (pay attention to the charset)
         * @return array of filled Task data objects (may have size 0) – doesn't return null
         * @throws IOException
         * @throws InvalidCalendarException on parser exceptions
         * @param reader where the iCalendar is taken from
         *
         * @return array of filled [Task] data objects (may have size 0)
         *
         * @throws ParserException when the iCalendar can't be parsed
         * @throws IllegalArgumentException when the iCalendar resource contains an invalid value
         * @throws IOException on I/O errors
         */
        fun fromReader(reader: Reader): List<Task> {
            val ical: Calendar
            try {
                ical = CalendarBuilder().build(reader)
            } catch(e: ParserException) {
                throw InvalidCalendarException("Couldn't parse iCalendar object", e)
            } catch(e: IllegalArgumentException) {
                throw InvalidCalendarException("iCalendar object contains invalid value", e)
            }

        fun tasksFromReader(reader: Reader): List<Task> {
            val ical = fromReader(reader)
            val vToDos = ical.getComponents<VToDo>(Component.VTODO)
            return vToDos.mapTo(LinkedList()) { this.fromVToDo(it) }
        }
+3 −3
Original line number Diff line number Diff line
@@ -21,9 +21,9 @@ class EventTest {
    fun testCalendarProperties() {
        javaClass.classLoader!!.getResourceAsStream("events/multiple.ics").use { stream ->
            val properties = mutableMapOf<String, String>()
            Event.fromReader(InputStreamReader(stream, Charsets.UTF_8), properties)
            Event.eventsFromReader(InputStreamReader(stream, Charsets.UTF_8), properties)
            assertEquals(1, properties.size)
            assertEquals("Test-Kalender", properties[Event.CALENDAR_NAME])
            assertEquals("Test-Kalender", properties[ICalendar.CALENDAR_NAME])
        }
    }

@@ -203,7 +203,7 @@ class EventTest {

    private fun parseCalendar(fname: String, charset: Charset = Charsets.UTF_8): List<Event> =
            javaClass.classLoader!!.getResourceAsStream("events/$fname").use { stream ->
                return Event.fromReader(InputStreamReader(stream, charset))
                return Event.eventsFromReader(InputStreamReader(stream, charset))
            }

}
+2 −2
Original line number Diff line number Diff line
@@ -110,14 +110,14 @@ class TaskTest {

    private fun parseCalendar(fname: String, charset: Charset = Charsets.UTF_8): Task {
        javaClass.classLoader!!.getResourceAsStream("tasks/$fname").use { stream ->
            return Task.fromReader(InputStreamReader(stream, charset)).first()
            return Task.tasksFromReader(InputStreamReader(stream, charset)).first()
        }
    }

    private fun regenerate(t: Task): Task {
        val os = ByteArrayOutputStream()
        t.write(os)
        return Task.fromReader(InputStreamReader(ByteArrayInputStream(os.toByteArray()), Charsets.UTF_8)).first()
        return Task.tasksFromReader(InputStreamReader(ByteArrayInputStream(os.toByteArray()), Charsets.UTF_8)).first()
    }
    
}