diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/JtxObjectBuilder.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/JtxObjectBuilder.kt index 01909dc5..7ae93baa 100644 --- a/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/JtxObjectBuilder.kt +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/JtxObjectBuilder.kt @@ -9,11 +9,14 @@ package at.bitfire.synctools.mapping.jtx import android.content.ContentValues import android.content.Entity import at.bitfire.synctools.icalendar.AssociatedComponents +import at.bitfire.synctools.mapping.jtx.builder.CategoriesBuilder import at.bitfire.synctools.mapping.jtx.builder.CollectionIdBuilder +import at.bitfire.synctools.mapping.jtx.builder.CommentsBuilder import at.bitfire.synctools.mapping.jtx.builder.DescriptionBuilder import at.bitfire.synctools.mapping.jtx.builder.JtxEntityBuilder import at.bitfire.synctools.mapping.jtx.builder.RecurrenceFieldsBuilder import at.bitfire.synctools.mapping.jtx.builder.RemindersBuilder +import at.bitfire.synctools.mapping.jtx.builder.ResourcesBuilder import at.bitfire.synctools.mapping.jtx.builder.SyncPropertiesBuilder import at.bitfire.synctools.mapping.jtx.builder.TimeFieldsBuilder import at.bitfire.synctools.storage.jtx.JtxObjectAndExceptions @@ -39,7 +42,10 @@ class JtxObjectBuilder( DescriptionBuilder(), RecurrenceFieldsBuilder(), TimeFieldsBuilder(), - RemindersBuilder() + RemindersBuilder(), + CategoriesBuilder(), + CommentsBuilder(), + ResourcesBuilder() ) fun build(component: AssociatedComponents): JtxObjectAndExceptions { diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilder.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilder.kt new file mode 100644 index 00000000..03eacba4 --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilder.kt @@ -0,0 +1,34 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.Entity +import androidx.core.content.contentValuesOf +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.Property +import net.fortuna.ical4j.model.component.CalendarComponent +import net.fortuna.ical4j.model.property.Categories + +class CategoriesBuilder : JtxEntityBuilder { + override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) { + for (categories in from.getProperties(Property.CATEGORIES)) { + for (category in categories.categories.texts) { + to.addSubValue( + JtxContract.JtxCategory.CONTENT_URI, + contentValuesOf( + JtxContract.JtxCategory.TEXT to category, + + // Note: Currently not supported + JtxContract.JtxCategory.ID to 0L, + JtxContract.JtxCategory.LANGUAGE to null, + JtxContract.JtxCategory.OTHER to null + ) + ) + } + } + } +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilder.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilder.kt new file mode 100644 index 00000000..40a2b0be --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilder.kt @@ -0,0 +1,45 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.Entity +import androidx.core.content.contentValuesOf +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.Parameter +import net.fortuna.ical4j.model.Property +import net.fortuna.ical4j.model.component.CalendarComponent +import net.fortuna.ical4j.model.parameter.AltRep +import net.fortuna.ical4j.model.parameter.Language +import net.fortuna.ical4j.model.property.Comment +import kotlin.jvm.optionals.getOrNull + +class CommentsBuilder : JtxEntityBuilder { + override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) { + for (comment in from.getProperties(Property.COMMENT)) { + val text = comment.value + val language = comment.getParameter(Parameter.LANGUAGE).getOrNull()?.value + val altRep = comment.getParameter(Parameter.ALTREP).getOrNull()?.value + + val otherParameters = comment.parameterList.removeAll( + Parameter.LANGUAGE, + Parameter.ALTREP + ) + val others = JtxContract.getJsonStringFromXParameters(otherParameters) + + to.addSubValue( + JtxContract.JtxComment.CONTENT_URI, + contentValuesOf( + JtxContract.JtxComment.TEXT to text, + JtxContract.JtxComment.ALTREP to altRep, + JtxContract.JtxComment.LANGUAGE to language, + JtxContract.JtxComment.OTHER to others, + JtxContract.JtxComment.ID to 0L + ) + ) + } + } +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilder.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilder.kt new file mode 100644 index 00000000..fbf3815d --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilder.kt @@ -0,0 +1,40 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.Entity +import androidx.core.content.contentValuesOf +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.Property +import net.fortuna.ical4j.model.component.CalendarComponent +import net.fortuna.ical4j.model.component.VJournal +import net.fortuna.ical4j.model.property.Resources + +class ResourcesBuilder : JtxEntityBuilder { + override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) { + if (from is VJournal) { + // VJOURNAL doesn't support the RESOURCES property + return + } + + for (resources in from.getProperties(Property.RESOURCES)) { + for (resource in resources.resources.texts) { + to.addSubValue( + JtxContract.JtxResource.CONTENT_URI, + contentValuesOf( + JtxContract.JtxResource.TEXT to resource, + + // Note: Currently not supported + JtxContract.JtxResource.ID to 0L, + JtxContract.JtxResource.LANGUAGE to null, + JtxContract.JtxResource.OTHER to null + ) + ) + } + } + } +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilderTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilderTest.kt new file mode 100644 index 00000000..eec3d9c1 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CategoriesBuilderTest.kt @@ -0,0 +1,43 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.ContentValues +import android.content.Entity +import at.bitfire.synctools.icalendar.plusAssign +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.TextList +import net.fortuna.ical4j.model.component.VJournal +import net.fortuna.ical4j.model.property.Categories +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class CategoriesBuilderTest { + + private val builder = CategoriesBuilder() + + @Test + fun happyPath() { + val output = Entity(ContentValues()) + val journal = VJournal().apply { + this += Categories(TextList("one", "two")) + } + + builder.build(from = journal, main = journal, output) + + assertEquals(2, output.subValues.size) + val first = output.subValues[0] + assertEquals(JtxContract.JtxCategory.CONTENT_URI, first.uri) + assertEquals("one", first.values.getAsString(JtxContract.JtxCategory.TEXT)) + val second = output.subValues[1] + assertEquals(JtxContract.JtxCategory.CONTENT_URI, second.uri) + assertEquals("two", second.values.getAsString(JtxContract.JtxCategory.TEXT)) + } +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilderTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilderTest.kt new file mode 100644 index 00000000..85dd3b18 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/CommentsBuilderTest.kt @@ -0,0 +1,93 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.ContentValues +import android.content.Entity +import androidx.core.content.contentValuesOf +import at.bitfire.synctools.icalendar.plusAssign +import at.bitfire.synctools.test.assertContentValuesEqual +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.ParameterList +import net.fortuna.ical4j.model.component.VJournal +import net.fortuna.ical4j.model.component.VToDo +import net.fortuna.ical4j.model.parameter.AltRep +import net.fortuna.ical4j.model.parameter.Language +import net.fortuna.ical4j.model.parameter.XParameter +import net.fortuna.ical4j.model.property.Comment +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import java.net.URI + +@RunWith(RobolectricTestRunner::class) +class CommentsBuilderTest { + private val builder = CommentsBuilder() + + @Test + fun `no comment`() { + val output = Entity(ContentValues()) + val journal = VJournal() + + builder.build(from = journal, main = journal, output) + + assertEquals(0, output.subValues.size) + } + + @Test + fun `single comment`() { + val output = Entity(ContentValues()) + val task = VToDo().apply { + this += Comment( + ParameterList( + listOf( + Language("en"), + AltRep(URI.create("https://domain.example/comment.txt")), + XParameter("X-PARAM", "x-value") + ) + ), + "comment" + ) + } + + builder.build(from = task, main = task, output) + + assertEquals(1, output.subValues.size) + val subValue = output.subValues.first() + assertEquals(JtxContract.JtxComment.CONTENT_URI, subValue.uri) + assertContentValuesEqual( + expected = contentValuesOf( + JtxContract.JtxComment.TEXT to "comment", + JtxContract.JtxComment.ALTREP to "https://domain.example/comment.txt", + JtxContract.JtxComment.LANGUAGE to "en", + JtxContract.JtxComment.OTHER to """{"X-PARAM":"x-value"}""", + JtxContract.JtxComment.ID to 0L + ), + actual = subValue.values + ) + } + + @Test + fun `multiple comments`() { + val output = Entity(ContentValues()) + val task = VToDo().apply { + this += Comment("one") + this += Comment("two") + } + + builder.build(from = task, main = task, output) + + assertEquals(2, output.subValues.size) + val first = output.subValues[0] + assertEquals(JtxContract.JtxComment.CONTENT_URI, first.uri) + assertEquals("one", first.values.getAsString(JtxContract.JtxComment.TEXT)) + val second = output.subValues[1] + assertEquals(JtxContract.JtxComment.CONTENT_URI, second.uri) + assertEquals("two", second.values.getAsString(JtxContract.JtxComment.TEXT)) + } +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilderTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilderTest.kt new file mode 100644 index 00000000..d5e62ffb --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/jtx/builder/ResourcesBuilderTest.kt @@ -0,0 +1,94 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.jtx.builder + +import android.content.ContentValues +import android.content.Entity +import androidx.core.content.contentValuesOf +import at.bitfire.synctools.icalendar.plusAssign +import at.bitfire.synctools.test.assertContentValuesEqual +import at.techbee.jtx.JtxContract +import net.fortuna.ical4j.model.component.VJournal +import net.fortuna.ical4j.model.component.VToDo +import net.fortuna.ical4j.model.property.Resources +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class ResourcesBuilderTest { + + private val builder = ResourcesBuilder() + + @Test + fun `VTODO without resources`() { + val output = Entity(ContentValues()) + val journal = VToDo() + + builder.build(from = journal, main = journal, output) + + assertEquals(0, output.subValues.size) + } + + @Test + fun `VTODO with single resource`() { + val output = Entity(ContentValues()) + val journal = VToDo().apply { + this += Resources("resource") + } + + builder.build(from = journal, main = journal, output) + + assertEquals(1, output.subValues.size) + val subValue = output.subValues.first() + assertEquals(JtxContract.JtxResource.CONTENT_URI, subValue.uri) + assertContentValuesEqual( + expected = contentValuesOf( + JtxContract.JtxResource.TEXT to "resource", + JtxContract.JtxResource.ID to 0L, + JtxContract.JtxResource.LANGUAGE to null, + JtxContract.JtxResource.OTHER to null + ), + actual = subValue.values + ) + } + + @Test + fun `VTODO with multiple resources`() { + val output = Entity(ContentValues()) + val journal = VToDo().apply { + this += Resources(listOf("one", "two")) + this += Resources("three") + } + + builder.build(from = journal, main = journal, output) + + assertEquals(3, output.subValues.size) + val first = output.subValues[0] + assertEquals(JtxContract.JtxResource.CONTENT_URI, first.uri) + assertEquals("one", first.values.getAsString(JtxContract.JtxResource.TEXT)) + val second = output.subValues[1] + assertEquals(JtxContract.JtxResource.CONTENT_URI, second.uri) + assertEquals("two", second.values.getAsString(JtxContract.JtxResource.TEXT)) + val third = output.subValues[2] + assertEquals(JtxContract.JtxResource.CONTENT_URI, third.uri) + assertEquals("three", third.values.getAsString(JtxContract.JtxResource.TEXT)) + } + + @Test + fun `VJOURNAL should ignore RESOURCES properties`() { + val output = Entity(ContentValues()) + val journal = VJournal().apply { + this += Resources(listOf("one", "two")) + } + + builder.build(from = journal, main = journal, output) + + assertEquals(0, output.subValues.size) + } +}