Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

Commit 432bdeb

Browse files
authored
[jtx] Add builders for categories, comments, resources (#405)
1 parent c21a72f commit 432bdeb

7 files changed

Lines changed: 356 additions & 1 deletion

File tree

lib/src/main/kotlin/at/bitfire/synctools/mapping/jtx/JtxObjectBuilder.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ package at.bitfire.synctools.mapping.jtx
99
import android.content.ContentValues
1010
import android.content.Entity
1111
import at.bitfire.synctools.icalendar.AssociatedComponents
12+
import at.bitfire.synctools.mapping.jtx.builder.CategoriesBuilder
1213
import at.bitfire.synctools.mapping.jtx.builder.CollectionIdBuilder
14+
import at.bitfire.synctools.mapping.jtx.builder.CommentsBuilder
1315
import at.bitfire.synctools.mapping.jtx.builder.DescriptionBuilder
1416
import at.bitfire.synctools.mapping.jtx.builder.JtxEntityBuilder
1517
import at.bitfire.synctools.mapping.jtx.builder.RecurrenceFieldsBuilder
1618
import at.bitfire.synctools.mapping.jtx.builder.RemindersBuilder
19+
import at.bitfire.synctools.mapping.jtx.builder.ResourcesBuilder
1720
import at.bitfire.synctools.mapping.jtx.builder.SyncPropertiesBuilder
1821
import at.bitfire.synctools.mapping.jtx.builder.TimeFieldsBuilder
1922
import at.bitfire.synctools.storage.jtx.JtxObjectAndExceptions
@@ -39,7 +42,10 @@ class JtxObjectBuilder(
3942
DescriptionBuilder(),
4043
RecurrenceFieldsBuilder(),
4144
TimeFieldsBuilder(),
42-
RemindersBuilder()
45+
RemindersBuilder(),
46+
CategoriesBuilder(),
47+
CommentsBuilder(),
48+
ResourcesBuilder()
4349
)
4450

4551
fun build(component: AssociatedComponents<CalendarComponent>): JtxObjectAndExceptions {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.Entity
10+
import androidx.core.content.contentValuesOf
11+
import at.techbee.jtx.JtxContract
12+
import net.fortuna.ical4j.model.Property
13+
import net.fortuna.ical4j.model.component.CalendarComponent
14+
import net.fortuna.ical4j.model.property.Categories
15+
16+
class CategoriesBuilder : JtxEntityBuilder {
17+
override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) {
18+
for (categories in from.getProperties<Categories>(Property.CATEGORIES)) {
19+
for (category in categories.categories.texts) {
20+
to.addSubValue(
21+
JtxContract.JtxCategory.CONTENT_URI,
22+
contentValuesOf(
23+
JtxContract.JtxCategory.TEXT to category,
24+
25+
// Note: Currently not supported
26+
JtxContract.JtxCategory.ID to 0L,
27+
JtxContract.JtxCategory.LANGUAGE to null,
28+
JtxContract.JtxCategory.OTHER to null
29+
)
30+
)
31+
}
32+
}
33+
}
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.Entity
10+
import androidx.core.content.contentValuesOf
11+
import at.techbee.jtx.JtxContract
12+
import net.fortuna.ical4j.model.Parameter
13+
import net.fortuna.ical4j.model.Property
14+
import net.fortuna.ical4j.model.component.CalendarComponent
15+
import net.fortuna.ical4j.model.parameter.AltRep
16+
import net.fortuna.ical4j.model.parameter.Language
17+
import net.fortuna.ical4j.model.property.Comment
18+
import kotlin.jvm.optionals.getOrNull
19+
20+
class CommentsBuilder : JtxEntityBuilder {
21+
override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) {
22+
for (comment in from.getProperties<Comment>(Property.COMMENT)) {
23+
val text = comment.value
24+
val language = comment.getParameter<Language>(Parameter.LANGUAGE).getOrNull()?.value
25+
val altRep = comment.getParameter<AltRep>(Parameter.ALTREP).getOrNull()?.value
26+
27+
val otherParameters = comment.parameterList.removeAll(
28+
Parameter.LANGUAGE,
29+
Parameter.ALTREP
30+
)
31+
val others = JtxContract.getJsonStringFromXParameters(otherParameters)
32+
33+
to.addSubValue(
34+
JtxContract.JtxComment.CONTENT_URI,
35+
contentValuesOf(
36+
JtxContract.JtxComment.TEXT to text,
37+
JtxContract.JtxComment.ALTREP to altRep,
38+
JtxContract.JtxComment.LANGUAGE to language,
39+
JtxContract.JtxComment.OTHER to others,
40+
JtxContract.JtxComment.ID to 0L
41+
)
42+
)
43+
}
44+
}
45+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.Entity
10+
import androidx.core.content.contentValuesOf
11+
import at.techbee.jtx.JtxContract
12+
import net.fortuna.ical4j.model.Property
13+
import net.fortuna.ical4j.model.component.CalendarComponent
14+
import net.fortuna.ical4j.model.component.VJournal
15+
import net.fortuna.ical4j.model.property.Resources
16+
17+
class ResourcesBuilder : JtxEntityBuilder {
18+
override fun build(from: CalendarComponent, main: CalendarComponent, to: Entity) {
19+
if (from is VJournal) {
20+
// VJOURNAL doesn't support the RESOURCES property
21+
return
22+
}
23+
24+
for (resources in from.getProperties<Resources>(Property.RESOURCES)) {
25+
for (resource in resources.resources.texts) {
26+
to.addSubValue(
27+
JtxContract.JtxResource.CONTENT_URI,
28+
contentValuesOf(
29+
JtxContract.JtxResource.TEXT to resource,
30+
31+
// Note: Currently not supported
32+
JtxContract.JtxResource.ID to 0L,
33+
JtxContract.JtxResource.LANGUAGE to null,
34+
JtxContract.JtxResource.OTHER to null
35+
)
36+
)
37+
}
38+
}
39+
}
40+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import at.bitfire.synctools.icalendar.plusAssign
12+
import at.techbee.jtx.JtxContract
13+
import net.fortuna.ical4j.model.TextList
14+
import net.fortuna.ical4j.model.component.VJournal
15+
import net.fortuna.ical4j.model.property.Categories
16+
import org.junit.Assert.assertEquals
17+
import org.junit.Test
18+
import org.junit.runner.RunWith
19+
import org.robolectric.RobolectricTestRunner
20+
21+
@RunWith(RobolectricTestRunner::class)
22+
class CategoriesBuilderTest {
23+
24+
private val builder = CategoriesBuilder()
25+
26+
@Test
27+
fun happyPath() {
28+
val output = Entity(ContentValues())
29+
val journal = VJournal().apply {
30+
this += Categories(TextList("one", "two"))
31+
}
32+
33+
builder.build(from = journal, main = journal, output)
34+
35+
assertEquals(2, output.subValues.size)
36+
val first = output.subValues[0]
37+
assertEquals(JtxContract.JtxCategory.CONTENT_URI, first.uri)
38+
assertEquals("one", first.values.getAsString(JtxContract.JtxCategory.TEXT))
39+
val second = output.subValues[1]
40+
assertEquals(JtxContract.JtxCategory.CONTENT_URI, second.uri)
41+
assertEquals("two", second.values.getAsString(JtxContract.JtxCategory.TEXT))
42+
}
43+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import androidx.core.content.contentValuesOf
12+
import at.bitfire.synctools.icalendar.plusAssign
13+
import at.bitfire.synctools.test.assertContentValuesEqual
14+
import at.techbee.jtx.JtxContract
15+
import net.fortuna.ical4j.model.ParameterList
16+
import net.fortuna.ical4j.model.component.VJournal
17+
import net.fortuna.ical4j.model.component.VToDo
18+
import net.fortuna.ical4j.model.parameter.AltRep
19+
import net.fortuna.ical4j.model.parameter.Language
20+
import net.fortuna.ical4j.model.parameter.XParameter
21+
import net.fortuna.ical4j.model.property.Comment
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
import org.junit.runner.RunWith
25+
import org.robolectric.RobolectricTestRunner
26+
import java.net.URI
27+
28+
@RunWith(RobolectricTestRunner::class)
29+
class CommentsBuilderTest {
30+
private val builder = CommentsBuilder()
31+
32+
@Test
33+
fun `no comment`() {
34+
val output = Entity(ContentValues())
35+
val journal = VJournal()
36+
37+
builder.build(from = journal, main = journal, output)
38+
39+
assertEquals(0, output.subValues.size)
40+
}
41+
42+
@Test
43+
fun `single comment`() {
44+
val output = Entity(ContentValues())
45+
val task = VToDo().apply {
46+
this += Comment(
47+
ParameterList(
48+
listOf(
49+
Language("en"),
50+
AltRep(URI.create("https://domain.example/comment.txt")),
51+
XParameter("X-PARAM", "x-value")
52+
)
53+
),
54+
"comment"
55+
)
56+
}
57+
58+
builder.build(from = task, main = task, output)
59+
60+
assertEquals(1, output.subValues.size)
61+
val subValue = output.subValues.first()
62+
assertEquals(JtxContract.JtxComment.CONTENT_URI, subValue.uri)
63+
assertContentValuesEqual(
64+
expected = contentValuesOf(
65+
JtxContract.JtxComment.TEXT to "comment",
66+
JtxContract.JtxComment.ALTREP to "https://domain.example/comment.txt",
67+
JtxContract.JtxComment.LANGUAGE to "en",
68+
JtxContract.JtxComment.OTHER to """{"X-PARAM":"x-value"}""",
69+
JtxContract.JtxComment.ID to 0L
70+
),
71+
actual = subValue.values
72+
)
73+
}
74+
75+
@Test
76+
fun `multiple comments`() {
77+
val output = Entity(ContentValues())
78+
val task = VToDo().apply {
79+
this += Comment("one")
80+
this += Comment("two")
81+
}
82+
83+
builder.build(from = task, main = task, output)
84+
85+
assertEquals(2, output.subValues.size)
86+
val first = output.subValues[0]
87+
assertEquals(JtxContract.JtxComment.CONTENT_URI, first.uri)
88+
assertEquals("one", first.values.getAsString(JtxContract.JtxComment.TEXT))
89+
val second = output.subValues[1]
90+
assertEquals(JtxContract.JtxComment.CONTENT_URI, second.uri)
91+
assertEquals("two", second.values.getAsString(JtxContract.JtxComment.TEXT))
92+
}
93+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.jtx.builder
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import androidx.core.content.contentValuesOf
12+
import at.bitfire.synctools.icalendar.plusAssign
13+
import at.bitfire.synctools.test.assertContentValuesEqual
14+
import at.techbee.jtx.JtxContract
15+
import net.fortuna.ical4j.model.component.VJournal
16+
import net.fortuna.ical4j.model.component.VToDo
17+
import net.fortuna.ical4j.model.property.Resources
18+
import org.junit.Assert.assertEquals
19+
import org.junit.Test
20+
import org.junit.runner.RunWith
21+
import org.robolectric.RobolectricTestRunner
22+
23+
@RunWith(RobolectricTestRunner::class)
24+
class ResourcesBuilderTest {
25+
26+
private val builder = ResourcesBuilder()
27+
28+
@Test
29+
fun `VTODO without resources`() {
30+
val output = Entity(ContentValues())
31+
val journal = VToDo()
32+
33+
builder.build(from = journal, main = journal, output)
34+
35+
assertEquals(0, output.subValues.size)
36+
}
37+
38+
@Test
39+
fun `VTODO with single resource`() {
40+
val output = Entity(ContentValues())
41+
val journal = VToDo().apply {
42+
this += Resources("resource")
43+
}
44+
45+
builder.build(from = journal, main = journal, output)
46+
47+
assertEquals(1, output.subValues.size)
48+
val subValue = output.subValues.first()
49+
assertEquals(JtxContract.JtxResource.CONTENT_URI, subValue.uri)
50+
assertContentValuesEqual(
51+
expected = contentValuesOf(
52+
JtxContract.JtxResource.TEXT to "resource",
53+
JtxContract.JtxResource.ID to 0L,
54+
JtxContract.JtxResource.LANGUAGE to null,
55+
JtxContract.JtxResource.OTHER to null
56+
),
57+
actual = subValue.values
58+
)
59+
}
60+
61+
@Test
62+
fun `VTODO with multiple resources`() {
63+
val output = Entity(ContentValues())
64+
val journal = VToDo().apply {
65+
this += Resources(listOf("one", "two"))
66+
this += Resources("three")
67+
}
68+
69+
builder.build(from = journal, main = journal, output)
70+
71+
assertEquals(3, output.subValues.size)
72+
val first = output.subValues[0]
73+
assertEquals(JtxContract.JtxResource.CONTENT_URI, first.uri)
74+
assertEquals("one", first.values.getAsString(JtxContract.JtxResource.TEXT))
75+
val second = output.subValues[1]
76+
assertEquals(JtxContract.JtxResource.CONTENT_URI, second.uri)
77+
assertEquals("two", second.values.getAsString(JtxContract.JtxResource.TEXT))
78+
val third = output.subValues[2]
79+
assertEquals(JtxContract.JtxResource.CONTENT_URI, third.uri)
80+
assertEquals("three", third.values.getAsString(JtxContract.JtxResource.TEXT))
81+
}
82+
83+
@Test
84+
fun `VJOURNAL should ignore RESOURCES properties`() {
85+
val output = Entity(ContentValues())
86+
val journal = VJournal().apply {
87+
this += Resources(listOf("one", "two"))
88+
}
89+
90+
builder.build(from = journal, main = journal, output)
91+
92+
assertEquals(0, output.subValues.size)
93+
}
94+
}

0 commit comments

Comments
 (0)