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

Commit d70e790

Browse files
authored
[jtx rewrite] Add handler for Comments (#417)
* Add handler for Comments * Fix addition calls
1 parent b8a1a6f commit d70e790

3 files changed

Lines changed: 249 additions & 1 deletion

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package at.bitfire.synctools.mapping.jtx
88

99
import android.content.Entity
1010
import at.bitfire.synctools.icalendar.AssociatedComponents
11+
import at.bitfire.synctools.mapping.jtx.handler.CommentsHandler
1112
import at.bitfire.synctools.mapping.jtx.handler.CategoriesHandler
1213
import at.bitfire.synctools.mapping.jtx.handler.DescriptionHandler
1314
import at.bitfire.synctools.mapping.jtx.handler.JtxFieldHandler
@@ -31,7 +32,8 @@ class JtxObjectHandler(
3132
) {
3233
private val fieldHandlers: Array<JtxFieldHandler> = arrayOf(
3334
CategoriesHandler(),
34-
DescriptionHandler(),
35+
CommentsHandler(),
36+
DescriptionHandler()
3537
)
3638

3739
/**
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.handler
8+
9+
import android.content.Entity
10+
import at.bitfire.synctools.icalendar.plusAssign
11+
import at.techbee.jtx.JtxContract
12+
import net.fortuna.ical4j.model.component.CalendarComponent
13+
import net.fortuna.ical4j.model.parameter.AltRep
14+
import net.fortuna.ical4j.model.parameter.Language
15+
import net.fortuna.ical4j.model.property.Comment
16+
17+
class CommentsHandler : JtxFieldHandler {
18+
override fun process(
19+
from: Entity,
20+
main: Entity,
21+
to: CalendarComponent
22+
) {
23+
val commentTexts = from.subValues
24+
.filter { it.uri == JtxContract.JtxComment.CONTENT_URI }
25+
26+
for (commentValues in commentTexts) {
27+
val text = commentValues.values.getAsString(JtxContract.JtxComment.TEXT) ?: continue
28+
29+
val comment = Comment(text)
30+
31+
commentValues.values.getAsString(JtxContract.JtxComment.ALTREP)?.let { altRep ->
32+
comment += AltRep(altRep)
33+
}
34+
commentValues.values.getAsString(JtxContract.JtxComment.LANGUAGE)?.let { lang ->
35+
comment += Language(lang)
36+
}
37+
commentValues.values.getAsString(JtxContract.JtxComment.OTHER)?.let { other ->
38+
val otherParameters = JtxContract.getXParametersFromJson(other)
39+
for (parameter in otherParameters) {
40+
comment += parameter
41+
}
42+
}
43+
44+
to += comment
45+
}
46+
}
47+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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.handler
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import android.net.Uri
12+
import androidx.core.content.contentValuesOf
13+
import at.techbee.jtx.JtxContract
14+
import net.fortuna.ical4j.model.Parameter
15+
import net.fortuna.ical4j.model.Property
16+
import net.fortuna.ical4j.model.component.VToDo
17+
import net.fortuna.ical4j.model.parameter.AltRep
18+
import net.fortuna.ical4j.model.parameter.Language
19+
import net.fortuna.ical4j.model.parameter.XParameter
20+
import net.fortuna.ical4j.model.property.Comment
21+
import org.junit.Assert.assertEquals
22+
import org.junit.Assert.assertNull
23+
import org.junit.Test
24+
import org.junit.runner.RunWith
25+
import org.robolectric.RobolectricTestRunner
26+
import kotlin.jvm.optionals.getOrNull
27+
28+
@RunWith(RobolectricTestRunner::class)
29+
class CommentsHandlerTest {
30+
31+
private val handler = CommentsHandler()
32+
33+
@Test
34+
fun `No comment sub-values`() {
35+
val input = Entity(ContentValues())
36+
val output = VToDo()
37+
38+
handler.process(from = input, main = input, to = output)
39+
40+
assertEquals(0, output.getProperties<Comment>(Property.COMMENT).size)
41+
}
42+
43+
@Test
44+
fun `Sub-values with a different URI are ignored`() {
45+
val input = Entity(ContentValues())
46+
input.addSubValue(
47+
Uri.parse("content://at.techbee.jtx/other"),
48+
contentValuesOf(JtxContract.JtxComment.TEXT to "should be ignored")
49+
)
50+
val output = VToDo()
51+
52+
handler.process(from = input, main = input, to = output)
53+
54+
assertEquals(0, output.getProperties<Comment>(Property.COMMENT).size)
55+
}
56+
57+
@Test
58+
fun `Single comment with text only`() {
59+
val input = Entity(ContentValues())
60+
input.addSubValue(
61+
JtxContract.JtxComment.CONTENT_URI,
62+
contentValuesOf(JtxContract.JtxComment.TEXT to "meeting notes")
63+
)
64+
val output = VToDo()
65+
66+
handler.process(from = input, main = input, to = output)
67+
68+
val comments = output.getProperties<Comment>(Property.COMMENT)
69+
assertEquals(1, comments.size)
70+
assertEquals("meeting notes", comments.first().value)
71+
assertNull(comments.first().getParameter<AltRep>(Parameter.ALTREP).getOrNull())
72+
assertNull(comments.first().getParameter<Language>(Parameter.LANGUAGE).getOrNull())
73+
}
74+
75+
@Test
76+
fun `Single comment with ALTREP`() {
77+
val altRepUri = "https://example.com/comment.txt"
78+
val input = Entity(ContentValues())
79+
input.addSubValue(
80+
JtxContract.JtxComment.CONTENT_URI,
81+
contentValuesOf(
82+
JtxContract.JtxComment.TEXT to "see link",
83+
JtxContract.JtxComment.ALTREP to altRepUri
84+
)
85+
)
86+
val output = VToDo()
87+
88+
handler.process(from = input, main = input, to = output)
89+
90+
val comments = output.getProperties<Comment>(Property.COMMENT)
91+
assertEquals(1, comments.size)
92+
assertEquals("see link", comments.first().value)
93+
assertEquals(altRepUri, comments.first().getParameter<AltRep>(Parameter.ALTREP).getOrNull()?.value)
94+
}
95+
96+
@Test
97+
fun `Single comment with LANGUAGE`() {
98+
val input = Entity(ContentValues())
99+
input.addSubValue(
100+
JtxContract.JtxComment.CONTENT_URI,
101+
contentValuesOf(
102+
JtxContract.JtxComment.TEXT to "Bemerkung",
103+
JtxContract.JtxComment.LANGUAGE to "de"
104+
)
105+
)
106+
val output = VToDo()
107+
108+
handler.process(from = input, main = input, to = output)
109+
110+
val comments = output.getProperties<Comment>(Property.COMMENT)
111+
assertEquals(1, comments.size)
112+
assertEquals("Bemerkung", comments.first().value)
113+
assertEquals("de", comments.first().getParameter<Language>(Parameter.LANGUAGE).getOrNull()?.value)
114+
}
115+
116+
@Test
117+
fun `Single comment with X-parameters in OTHER`() {
118+
val input = Entity(ContentValues())
119+
input.addSubValue(
120+
JtxContract.JtxComment.CONTENT_URI,
121+
contentValuesOf(
122+
JtxContract.JtxComment.TEXT to "annotated",
123+
JtxContract.JtxComment.OTHER to """{"X-CUSTOM":"custom-value"}"""
124+
)
125+
)
126+
val output = VToDo()
127+
128+
handler.process(from = input, main = input, to = output)
129+
130+
val comments = output.getProperties<Comment>(Property.COMMENT)
131+
assertEquals(1, comments.size)
132+
assertEquals("annotated", comments.first().value)
133+
assertEquals(
134+
"custom-value",
135+
comments.first().getParameter<XParameter>("X-CUSTOM").getOrNull()?.value
136+
)
137+
}
138+
139+
@Test
140+
fun `Single comment with all parameters`() {
141+
val altRepUri = "https://example.com/full-comment.txt"
142+
val input = Entity(ContentValues())
143+
input.addSubValue(
144+
JtxContract.JtxComment.CONTENT_URI,
145+
contentValuesOf(
146+
JtxContract.JtxComment.TEXT to "full comment",
147+
JtxContract.JtxComment.ALTREP to altRepUri,
148+
JtxContract.JtxComment.LANGUAGE to "en",
149+
JtxContract.JtxComment.OTHER to """{"X-EXTRA":"extra-value"}"""
150+
)
151+
)
152+
val output = VToDo()
153+
154+
handler.process(from = input, main = input, to = output)
155+
156+
val comments = output.getProperties<Comment>(Property.COMMENT)
157+
assertEquals(1, comments.size)
158+
val comment = comments.first()
159+
assertEquals("full comment", comment.value)
160+
assertEquals(altRepUri, comment.getParameter<AltRep>(Parameter.ALTREP).getOrNull()?.value)
161+
assertEquals("en", comment.getParameter<Language>(Parameter.LANGUAGE).getOrNull()?.value)
162+
assertEquals("extra-value", comment.getParameter<XParameter>("X-EXTRA").getOrNull()?.value)
163+
}
164+
165+
@Test
166+
fun `Multiple comments`() {
167+
val input = Entity(ContentValues())
168+
input.addSubValue(
169+
JtxContract.JtxComment.CONTENT_URI,
170+
contentValuesOf(JtxContract.JtxComment.TEXT to "first")
171+
)
172+
input.addSubValue(
173+
JtxContract.JtxComment.CONTENT_URI,
174+
contentValuesOf(JtxContract.JtxComment.TEXT to "second")
175+
)
176+
val output = VToDo()
177+
178+
handler.process(from = input, main = input, to = output)
179+
180+
val comments = output.getProperties<Comment>(Property.COMMENT)
181+
assertEquals(2, comments.size)
182+
assertEquals("first", comments[0].value)
183+
assertEquals("second", comments[1].value)
184+
}
185+
186+
@Test
187+
fun `Comment with missing TEXT is skipped`() {
188+
val input = Entity(ContentValues())
189+
input.addSubValue(
190+
JtxContract.JtxComment.CONTENT_URI,
191+
contentValuesOf(JtxContract.JtxComment.LANGUAGE to "en")
192+
)
193+
val output = VToDo()
194+
195+
handler.process(from = input, main = input, to = output)
196+
197+
assertEquals(0, output.getProperties<Comment>(Property.COMMENT).size)
198+
}
199+
}

0 commit comments

Comments
 (0)