@@ -38,8 +38,7 @@ import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.TYPES
38
38
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.VERSION
39
39
import org.jetbrains.kotlinx.dataframe.io.ARRAY_COLUMN_NAME
40
40
import org.jetbrains.kotlinx.dataframe.io.Base64ImageEncodingOptions
41
- import org.jetbrains.kotlinx.dataframe.io.DataframeConvertableEncodingOptions
42
- import org.jetbrains.kotlinx.dataframe.io.EncodingOptions
41
+ import org.jetbrains.kotlinx.dataframe.io.CustomEncoder
43
42
import org.jetbrains.kotlinx.dataframe.io.VALUE_COLUMN_NAME
44
43
import org.jetbrains.kotlinx.dataframe.jupyter.KotlinNotebookPluginUtils
45
44
import org.jetbrains.kotlinx.dataframe.jupyter.KotlinNotebookPluginUtils.isDataframeConvertable
@@ -53,7 +52,7 @@ import java.io.IOException
53
52
54
53
// See docs/serialization_format.md for a description of
55
54
// serialization versions and format.
56
- internal const val SERIALIZATION_VERSION = " 2.1.0 "
55
+ internal const val SERIALIZATION_VERSION = " 2.1.1 "
57
56
58
57
internal object SerializationKeys {
59
58
const val DATA = " data"
@@ -113,14 +112,14 @@ internal fun encodeRowWithMetadata(
113
112
frame : ColumnsContainer <* >,
114
113
index : Int ,
115
114
rowLimit : Int? = null,
116
- encodingOptions : List <EncodingOptions > ,
115
+ customEncoders : List <CustomEncoder > = emptyList() ,
117
116
): JsonElement ? {
118
117
val values: List <Pair <String , JsonElement >> = frame.columns().map { col ->
119
118
when (col) {
120
119
is ColumnGroup <* > -> {
121
120
val schema = col.schema()
122
121
buildJsonObject {
123
- put(DATA , encodeRowWithMetadata(col, index, rowLimit, encodingOptions ) ? : JsonPrimitive (null ))
122
+ put(DATA , encodeRowWithMetadata(col, index, rowLimit, customEncoders ) ? : JsonPrimitive (null ))
124
123
putJsonObject(METADATA ) {
125
124
put(KIND , JsonPrimitive (ColumnKind .Group .toString()))
126
125
put(COLUMNS , Json .encodeToJsonElement(schema.columns.keys))
@@ -137,9 +136,9 @@ internal fun encodeRowWithMetadata(
137
136
138
137
is FrameColumn <* > -> {
139
138
val data = if (rowLimit == null ) {
140
- encodeFrameWithMetadata(col[index], null , encodingOptions )
139
+ encodeFrameWithMetadata(col[index], null , customEncoders )
141
140
} else {
142
- encodeFrameWithMetadata(col[index].take(rowLimit), rowLimit, encodingOptions )
141
+ encodeFrameWithMetadata(col[index].take(rowLimit), rowLimit, customEncoders )
143
142
}
144
143
val schema = col.schema.value
145
144
buildJsonObject {
@@ -160,32 +159,18 @@ internal fun encodeRowWithMetadata(
160
159
}
161
160
}
162
161
163
- else -> encodeValue(col, index, encodingOptions )
162
+ else -> encodeValue(col, index, customEncoders )
164
163
}.let { col.name to it }
165
164
}
166
165
if (values.isEmpty()) return null
167
166
return JsonObject (values.toMap())
168
167
}
169
168
170
- internal fun encodeValue (col : AnyCol , index : Int , encodingOptions : List <EncodingOptions > ): JsonElement =
169
+ internal fun encodeValue (col : AnyCol , index : Int , customEncoders : List <CustomEncoder > = emptyList() ): JsonElement =
171
170
when {
172
- isDataframeConvertable(col[index]) && encodingOptions.get<DataframeConvertableEncodingOptions >() != null ->
173
- if (col[index] == null ) {
174
- JsonPrimitive (null )
175
- } else {
176
- val options = encodingOptions.get<DataframeConvertableEncodingOptions >()!!
177
- val data = encodeFrameWithMetadata(
178
- KotlinNotebookPluginUtils .convertToDataFrame(col[index]!! ),
179
- options.rowsLimit,
180
- encodingOptions,
181
- )
182
- buildJsonObject {
183
- put(DATA , data)
184
- putJsonObject(METADATA ) {
185
- put(KIND , JsonPrimitive (CellKind .DataFrameConvertable .toString()))
186
- }
187
- }
188
- }
171
+ customEncoders.any { it.canEncode(col[index]) } -> {
172
+ customEncoders.first { it.canEncode(col[index]) }.encode(col[index])
173
+ }
189
174
190
175
col.isList() -> col[index]?.let { list ->
191
176
val values = (list as List <* >).map { convert(it) }
@@ -194,43 +179,61 @@ internal fun encodeValue(col: AnyCol, index: Int, encodingOptions: List<Encoding
194
179
195
180
col.typeClass in valueTypes -> convert(col[index])
196
181
197
- col.typeClass == BufferedImage ::class && encodingOptions.get<Base64ImageEncodingOptions >() != null ->
198
- col[index]?.let { image ->
199
- JsonPrimitive (
200
- encodeBufferedImageAsBase64(
201
- image as BufferedImage ,
202
- encodingOptions.get<Base64ImageEncodingOptions >()!! ,
203
- ),
204
- )
205
- } ? : JsonPrimitive (" " )
206
-
207
182
else -> JsonPrimitive (col[index]?.toString())
208
183
}
209
184
210
- @Suppress(" UNCHECKED_CAST" )
211
- private inline fun <reified T : EncodingOptions > List<EncodingOptions>.get (): T ? = this .find { it is T } as T ?
212
-
213
- private fun encodeBufferedImageAsBase64 (
214
- image : BufferedImage ,
215
- imageEncodingOptions : Base64ImageEncodingOptions = Base64ImageEncodingOptions (),
216
- ): String? =
217
- try {
218
- val preparedImage = if (imageEncodingOptions.isLimitSizeOn) {
219
- image.resizeKeepingAspectRatio(imageEncodingOptions.imageSizeLimit)
220
- } else {
221
- image
222
- }
185
+ internal class DataframeConvertableEncoder (
186
+ private val encoders : List <CustomEncoder >,
187
+ private val rowLimit : Int? = null ,
188
+ ) : CustomEncoder {
189
+ override fun canEncode (input : Any? ): Boolean = isDataframeConvertable(input)
190
+
191
+ override fun encode (input : Any? ): JsonElement =
192
+ input?.let {
193
+ val data = encodeFrameWithMetadata(
194
+ KotlinNotebookPluginUtils .convertToDataFrame(input),
195
+ rowLimit,
196
+ encoders,
197
+ )
198
+ buildJsonObject {
199
+ put(DATA , data)
200
+ putJsonObject(METADATA ) {
201
+ put(KIND , JsonPrimitive (CellKind .DataFrameConvertable .toString()))
202
+ }
203
+ }
204
+ } ? : JsonPrimitive (null )
205
+ }
223
206
224
- val bytes = if (imageEncodingOptions.isGzipOn) {
225
- preparedImage.toByteArray().encodeGzip()
226
- } else {
227
- preparedImage.toByteArray()
228
- }
207
+ internal class BufferedImageEncoder (private val options : Base64ImageEncodingOptions ) : CustomEncoder {
208
+ override fun canEncode (input : Any? ): Boolean = input is BufferedImage
229
209
230
- bytes.toBase64()
231
- } catch (e: IOException ) {
232
- null
233
- }
210
+ override fun encode (input : Any? ): JsonElement =
211
+ JsonPrimitive (
212
+ input?.let { image -> encodeBufferedImageAsBase64(image as BufferedImage , options) } ? : " " ,
213
+ )
214
+
215
+ private fun encodeBufferedImageAsBase64 (
216
+ image : BufferedImage ,
217
+ imageEncodingOptions : Base64ImageEncodingOptions = Base64ImageEncodingOptions (),
218
+ ): String =
219
+ try {
220
+ val preparedImage = if (imageEncodingOptions.isLimitSizeOn) {
221
+ image.resizeKeepingAspectRatio(imageEncodingOptions.imageSizeLimit)
222
+ } else {
223
+ image
224
+ }
225
+
226
+ val bytes = if (imageEncodingOptions.isGzipOn) {
227
+ preparedImage.toByteArray().encodeGzip()
228
+ } else {
229
+ preparedImage.toByteArray()
230
+ }
231
+
232
+ bytes.toBase64()
233
+ } catch (_: IOException ) {
234
+ " "
235
+ }
236
+ }
234
237
235
238
private fun createJsonTypeDescriptor (columnSchema : ColumnSchema ): JsonObject =
236
239
JsonObject (
@@ -244,7 +247,7 @@ private fun createJsonTypeDescriptor(columnSchema: ColumnSchema): JsonObject =
244
247
internal fun encodeFrameWithMetadata (
245
248
frame : AnyFrame ,
246
249
rowLimit : Int? = null,
247
- encodingOptions : List <EncodingOptions > ,
250
+ customEncoders : List <CustomEncoder > = emptyList() ,
248
251
): JsonArray {
249
252
val valueColumn = frame.extractValueColumn()
250
253
val arrayColumn = frame.extractArrayColumn()
@@ -258,13 +261,13 @@ internal fun encodeFrameWithMetadata(
258
261
encodeFrameWithMetadata(
259
262
it as AnyFrame ,
260
263
rowLimit,
261
- encodingOptions ,
264
+ customEncoders ,
262
265
)
263
266
} else {
264
267
null
265
268
}
266
269
}
267
- ? : encodeRowWithMetadata(frame, rowIndex, rowLimit, encodingOptions )
270
+ ? : encodeRowWithMetadata(frame, rowIndex, rowLimit, customEncoders )
268
271
}
269
272
270
273
return buildJsonArray { addAll(data.map { convert(it) }) }
@@ -372,7 +375,7 @@ internal fun encodeDataFrameWithMetadata(
372
375
frame : AnyFrame ,
373
376
rowLimit : Int ,
374
377
nestedRowLimit : Int? = null,
375
- encodingOptions : List <EncodingOptions > ,
378
+ customEncoders : List <CustomEncoder > = emptyList() ,
376
379
): JsonObject =
377
380
buildJsonObject {
378
381
put(VERSION , JsonPrimitive (SERIALIZATION_VERSION ))
@@ -393,7 +396,7 @@ internal fun encodeDataFrameWithMetadata(
393
396
encodeFrameWithMetadata(
394
397
frame = frame.take(rowLimit),
395
398
rowLimit = nestedRowLimit,
396
- encodingOptions = encodingOptions ,
399
+ customEncoders = customEncoders ,
397
400
),
398
401
)
399
402
}
0 commit comments