@@ -2,6 +2,8 @@ package org.jetbrains.compose.resources
2
2
3
3
import com.squareup.kotlinpoet.*
4
4
import java.nio.file.Path
5
+ import java.util.SortedMap
6
+ import java.util.TreeMap
5
7
import kotlin.io.path.invariantSeparatorsPathString
6
8
7
9
internal enum class ResourceType (val typeName : String ) {
@@ -26,12 +28,14 @@ internal data class ResourceItem(
26
28
val path : Path
27
29
)
28
30
29
- private fun ResourceItem .getClassName (): ClassName = when (type ) {
31
+ private fun ResourceType .getClassName (): ClassName = when (this ) {
30
32
ResourceType .DRAWABLE -> ClassName (" org.jetbrains.compose.resources" , " DrawableResource" )
31
33
ResourceType .STRING -> ClassName (" org.jetbrains.compose.resources" , " StringResource" )
32
34
ResourceType .FONT -> ClassName (" org.jetbrains.compose.resources" , " FontResource" )
33
35
}
34
36
37
+ private val resourceItemClass = ClassName (" org.jetbrains.compose.resources" , " ResourceItem" )
38
+
35
39
private fun CodeBlock.Builder.addQualifiers (resourceItem : ResourceItem ): CodeBlock .Builder {
36
40
val languageQualifier = ClassName (" org.jetbrains.compose.resources" , " LanguageQualifier" )
37
41
val regionQualifier = ClassName (" org.jetbrains.compose.resources" , " RegionQualifier" )
@@ -101,85 +105,118 @@ internal fun getResFileSpec(
101
105
// type -> id -> items
102
106
resources : Map <ResourceType , Map <String , List <ResourceItem >>>,
103
107
packageName : String
104
- ): FileSpec = FileSpec .builder(packageName, " Res" ).apply {
105
- addType(TypeSpec .objectBuilder(" Res" ).apply {
106
- addModifiers(KModifier .INTERNAL )
108
+ ): FileSpec =
109
+ FileSpec .builder(packageName, " Res" ).apply {
107
110
addAnnotation(
108
111
AnnotationSpec .builder(ClassName (" kotlin" , " OptIn" ))
109
112
.addMember(" org.jetbrains.compose.resources.InternalResourceApi::class" )
110
- .build()
111
- )
112
- addAnnotation(
113
- AnnotationSpec .builder(ClassName (" org.jetbrains.compose.resources" , " ExperimentalResourceApi" ))
113
+ .addMember(" org.jetbrains.compose.resources.ExperimentalResourceApi::class" )
114
114
.build()
115
115
)
116
116
117
- // readFileBytes
118
- val readResourceBytes = MemberName (" org.jetbrains.compose.resources" , " readResourceBytes" )
119
- addFunction(
120
- FunSpec .builder(" readBytes" )
121
- .addKdoc("""
117
+ // we need to sort it to generate the same code on different platforms
118
+ val sortedResources = sortResources(resources)
119
+
120
+ addType(TypeSpec .objectBuilder(" Res" ).apply {
121
+ addModifiers(KModifier .INTERNAL )
122
+ addAnnotation(
123
+ AnnotationSpec .builder(
124
+ ClassName (" org.jetbrains.compose.resources" , " ExperimentalResourceApi" )
125
+ ).build()
126
+ )
127
+
128
+ // readFileBytes
129
+ val readResourceBytes = MemberName (" org.jetbrains.compose.resources" , " readResourceBytes" )
130
+ addFunction(
131
+ FunSpec .builder(" readBytes" )
132
+ .addKdoc(
133
+ """
122
134
Reads the content of the resource file at the specified path and returns it as a byte array.
123
135
124
136
Example: `val bytes = Res.readBytes("files/key.bin")`
125
137
126
138
@param path The path of the file to read in the compose resource's directory.
127
139
@return The content of the file as a byte array.
128
- """ .trimIndent())
129
- .addParameter(" path" , String ::class )
130
- .addModifiers(KModifier .SUSPEND )
131
- .returns(ByteArray ::class )
132
- .addStatement(" return %M(path)" , readResourceBytes) // todo: add module ID here
133
- .build()
134
- )
140
+ """ .trimIndent()
141
+ )
142
+ .addParameter(" path" , String ::class )
143
+ .addModifiers(KModifier .SUSPEND )
144
+ .returns(ByteArray ::class )
145
+ .addStatement(" return %M(path)" , readResourceBytes) // todo: add module ID here
146
+ .build()
147
+ )
148
+ val types = sortedResources.map { (type, idToResources) ->
149
+ getResourceTypeObject(type, idToResources)
150
+ }
151
+ addTypes(types)
152
+ }.build())
135
153
136
- val types = resources.map { (type, idToResources) ->
137
- getResourceTypeObject(type, idToResources)
138
- }.sortedBy { it.name }
139
- addTypes(types)
140
- }.build())
141
- }.build()
154
+ sortedResources
155
+ .flatMap { (type, idToResources) ->
156
+ idToResources.map { (name, items) ->
157
+ getResourceInitializer(name, type, items)
158
+ }
159
+ }
160
+ .forEach { addFunction(it) }
161
+ }.build()
142
162
143
163
private fun getResourceTypeObject (type : ResourceType , nameToResources : Map <String , List <ResourceItem >>) =
144
164
TypeSpec .objectBuilder(type.typeName).apply {
145
- nameToResources.entries
146
- .sortedBy { it.key }
147
- .forEach { (name, items) ->
148
- addResourceProperty(name, items.sortedBy { it.path })
165
+ nameToResources.keys
166
+ .forEach { name ->
167
+ addProperty(
168
+ PropertySpec
169
+ .builder(name, type.getClassName())
170
+ .initializer(" get_$name ()" )
171
+ .build()
172
+ )
149
173
}
150
174
}.build()
151
175
152
- private fun TypeSpec.Builder.addResourceProperty (name : String , items : List <ResourceItem >) {
153
- val resourceItemClass = ClassName (" org.jetbrains.compose.resources" , " ResourceItem" )
154
-
155
- val first = items.first()
156
- val propertyClassName = first.getClassName()
157
- val resourceId = first.let { " ${it.type} :${it.name} " }
158
-
159
- val initializer = CodeBlock .builder()
160
- .add(" %T(\n " , propertyClassName).withIndent {
161
- add(" \" $resourceId \" ,\n " )
162
- if (first.type == ResourceType .STRING ) {
163
- add(" \" ${first.name} \" ,\n " )
164
- }
165
- add(" setOf(\n " ).withIndent {
166
- items.forEach { item ->
167
- add(" %T(\n " , resourceItemClass).withIndent {
168
- add(" setOf(" ).addQualifiers(item).add(" ),\n " )
169
- // file separator should be '/' on all platforms
170
- add(" \" ${item.path.invariantSeparatorsPathString} \"\n " ) // todo: add module ID here
176
+ private fun getResourceInitializer (name : String , type : ResourceType , items : List <ResourceItem >): FunSpec {
177
+ val propertyTypeName = type.getClassName()
178
+ val resourceId = " ${type} :${name} "
179
+ return FunSpec .builder(" get_$name " )
180
+ .addModifiers(KModifier .PRIVATE )
181
+ .returns(propertyTypeName)
182
+ .addStatement(
183
+ CodeBlock .builder()
184
+ .add(" return %T(\n " , propertyTypeName).withIndent {
185
+ add(" \" $resourceId \" ," )
186
+ if (type == ResourceType .STRING ) add(" \" $name \" ," )
187
+ withIndent {
188
+ add(" \n setOf(\n " ).withIndent {
189
+ items.forEach { item ->
190
+ add(" %T(" , resourceItemClass)
191
+ add(" setOf(" ).addQualifiers(item).add(" ), " )
192
+ // file separator should be '/' on all platforms
193
+ add(" \" ${item.path.invariantSeparatorsPathString} \" " ) // todo: add module ID here
194
+ add(" ),\n " )
195
+ }
196
+ }
197
+ add(" )\n " )
171
198
}
172
- add(" ),\n " )
173
199
}
174
- }
175
- add(" )\n " )
176
- }
177
- .add(" )" )
200
+ .add(" )" )
201
+ .build().toString()
202
+ )
178
203
.build()
204
+ }
179
205
180
- addProperty(
181
- PropertySpec .builder(name, propertyClassName)
182
- .initializer(initializer)
183
- .build()
184
- )
206
+ private fun sortResources (
207
+ resources : Map <ResourceType , Map <String , List <ResourceItem >>>
208
+ ): TreeMap <ResourceType , TreeMap <String , List <ResourceItem >>> {
209
+ val result = TreeMap <ResourceType , TreeMap <String , List <ResourceItem >>>()
210
+ resources
211
+ .entries
212
+ .forEach { (type, items) ->
213
+ val typeResult = TreeMap <String , List <ResourceItem >>()
214
+ items
215
+ .entries
216
+ .forEach { (name, resItems) ->
217
+ typeResult[name] = resItems.sortedBy { it.path }
218
+ }
219
+ result[type] = typeResult
220
+ }
221
+ return result
185
222
}
0 commit comments