Skip to content

Commit 80e0cac

Browse files
authored
chore: [ANDROSDK-2339] migrate RepositoryScope to Kotlin (#2656)
2 parents 64cc167 + f317056 commit 80e0cac

8 files changed

Lines changed: 128 additions & 179 deletions

File tree

core/api/core.api

Lines changed: 41 additions & 61 deletions
Large diffs are not rendered by default.

core/src/main/java/org/hisp/dhis/android/core/arch/repositories/scope/RepositoryScope.java

Lines changed: 0 additions & 105 deletions
This file was deleted.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2004-2025, University of Oslo
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* Redistributions of source code must retain the above copyright notice, this
8+
* list of conditions and the following disclaimer.
9+
*
10+
* Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
* Neither the name of the HISP project nor the names of its contributors may
14+
* be used to endorse or promote products derived from this software without
15+
* specific prior written permission.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
package org.hisp.dhis.android.core.arch.repositories.scope
29+
30+
import org.hisp.dhis.android.annotations.ModelBuilder
31+
import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenSelection
32+
import org.hisp.dhis.android.core.arch.repositories.scope.internal.RepositoryScopeComplexFilterItem
33+
import org.hisp.dhis.android.core.arch.repositories.scope.internal.RepositoryScopeFilterItem
34+
import org.hisp.dhis.android.core.arch.repositories.scope.internal.RepositoryScopeOrderByItem
35+
36+
@ConsistentCopyVisibility
37+
@ModelBuilder
38+
data class RepositoryScope internal constructor(
39+
val filters: List<RepositoryScopeFilterItem>,
40+
val complexFilters: List<RepositoryScopeComplexFilterItem>,
41+
internal val orderBy: List<RepositoryScopeOrderByItem>,
42+
internal val children: ChildrenSelection,
43+
) {
44+
enum class OrderByDirection(val api: String) {
45+
ASC("asc"),
46+
DESC("desc"),
47+
}
48+
49+
fun filters(): List<RepositoryScopeFilterItem> = filters
50+
fun complexFilters(): List<RepositoryScopeComplexFilterItem> = complexFilters
51+
internal fun orderBy(): List<RepositoryScopeOrderByItem> = orderBy
52+
internal fun children(): ChildrenSelection = children
53+
54+
fun hasFilters(): Boolean = filters.isNotEmpty() || complexFilters.isNotEmpty()
55+
56+
fun toBuilder(): Builder = RepositoryScopeBuilder.from(this)
57+
58+
class Builder : RepositoryScopeBuilder()
59+
60+
companion object {
61+
@JvmStatic
62+
fun empty(): RepositoryScope = builder()
63+
.children(ChildrenSelection.empty())
64+
.filters(emptyList())
65+
.complexFilters(emptyList())
66+
.orderBy(emptyList())
67+
.build()
68+
69+
@JvmStatic
70+
fun builder(): Builder = Builder()
71+
}
72+
}

core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageHandler.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import org.hisp.dhis.android.core.attribute.ProgramStageAttributeValueLink
3434
import org.hisp.dhis.android.core.attribute.internal.ProgramStageAttributeValueLinkHandler
3535
import org.hisp.dhis.android.core.common.ObjectWithUid
3636
import org.hisp.dhis.android.core.program.ProgramStage
37-
import org.hisp.dhis.android.core.program.ProgramStageSection
3837
import org.koin.core.annotation.Singleton
3938

4039
@Singleton
@@ -52,11 +51,7 @@ internal class ProgramStageHandler(
5251
programStageDataElementHandler.handleMany(o.programStageDataElements())
5352
programStageSectionHandler.handleMany(
5453
o.programStageSections(),
55-
) { programStageSection: ProgramStageSection ->
56-
programStageSection.toBuilder()
57-
.programStage(ObjectWithUid.create(o.uid()))
58-
.build()
59-
}
54+
)
6055

6156
if (action === HandleAction.Update) {
6257
programStageDataElementCleaner.deleteOrphan(o, o.programStageDataElements())

core/src/main/java/org/hisp/dhis/android/network/programstage/ProgramStageDTO.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ internal data class ProgramStageDTO(
113113
minDaysFromStart(minDaysFromStart)
114114
standardInterval(standardInterval)
115115
enableUserAssignment?.let { enableUserAssignment(it) }
116-
programStageSections(programStageSections?.map { it.toDomain() })
116+
programStageSections(programStageSections?.map { it.toDomain(id) })
117117
programStageDataElements(programStageDataElements?.map { it.toDomain() })
118118
periodType(periodType?.let { PeriodType.valueOf(it) })
119119
program(program?.toDomain())

core/src/main/java/org/hisp/dhis/android/network/programstage/ProgramStageSectionDTO.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
package org.hisp.dhis.android.network.programstage
3030

3131
import kotlinx.serialization.Serializable
32+
import org.hisp.dhis.android.core.common.ObjectWithUid
3233
import org.hisp.dhis.android.core.program.ProgramStageSection
3334
import org.hisp.dhis.android.network.common.dto.BaseIdentifiableObjectDTO
3435
import org.hisp.dhis.android.network.common.dto.ObjectWithUidDTO
@@ -53,14 +54,14 @@ internal data class ProgramStageSectionDTO(
5354
val description: String?,
5455
val displayDescription: String?,
5556
) : BaseIdentifiableObjectDTO {
56-
fun toDomain(): ProgramStageSection {
57+
fun toDomain(programStageUid: String): ProgramStageSection {
5758
return ProgramStageSection.builder()
5859
.applyBaseIdentifiableFields(this)
5960
.sortOrder(sortOrder)
6061
.programIndicators(programIndicators?.map { it.toDomain() })
6162
.dataElements(dataElements?.map { it.toDomain() })
6263
.renderType(renderType?.toDomain())
63-
.programStage(programStage!!.toDomain())
64+
.programStage(ObjectWithUid.create(programStageUid))
6465
.description(description)
6566
.displayDescription(displayDescription)
6667
.build()

core/src/test/java/org/hisp/dhis/android/core/program/internal/ProgramStageHandlerShould.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ProgramStageHandlerShould {
109109
@Throws(Exception::class)
110110
fun call_program_stage_section_handler() = runTest {
111111
programStageHandler.handle(programStage)
112-
verify(programStageSectionHandler).handleMany(eq(programStageSections), any())
112+
verify(programStageSectionHandler).handleMany(eq(programStageSections))
113113
}
114114

115115
@Test

processor/src/main/java/org/hisp/dhis/android/processor/ModelBuilderProcessor.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,17 @@ class ModelBuilderProcessor(
184184
.contains(field.type.toString())
185185
val isNotNull = !field.type.isMarkedNullable
186186
val type = renderType(field.type)
187+
// An internal field may reference an internal type. A `protected` backing property in a
188+
// public builder would leak that internal type, so use `private` visibility instead. It
189+
// must stay `private` (not `internal`): the backing property is only used by the generated
190+
// setters and build() within this class, and a module-visible `internal` property would
191+
// shadow identically-named properties inside callers' `builder().apply { }` blocks.
192+
val visibility = if (field.isInternal) "private" else "protected"
187193

188194
return when {
189-
isNotNull && isPrimitive -> "protected var ${field.name} by Delegates.notNull<$type>()"
190-
isNotNull -> "protected lateinit var ${field.name}: $type"
191-
else -> "protected var ${field.name}: $type = null"
195+
isNotNull && isPrimitive -> "$visibility var ${field.name} by Delegates.notNull<$type>()"
196+
isNotNull -> "$visibility lateinit var ${field.name}: $type"
197+
else -> "$visibility var ${field.name}: $type = null"
192198
}
193199
}
194200

0 commit comments

Comments
 (0)