3
3
import com .google .common .base .Preconditions ;
4
4
import com .google .common .reflect .TypeToken ;
5
5
import lombok .NonNull ;
6
- import org .slf4j .Logger ;
7
- import org .slf4j .LoggerFactory ;
8
6
import tech .ydb .yoj .ExperimentalApi ;
9
7
import tech .ydb .yoj .databind .converter .StringValueConverter ;
10
8
import tech .ydb .yoj .databind .converter .ValueConverter ;
11
9
import tech .ydb .yoj .databind .schema .Column ;
12
10
import tech .ydb .yoj .databind .schema .CustomConverterException ;
11
+ import tech .ydb .yoj .databind .schema .CustomValueTypeInfo ;
13
12
import tech .ydb .yoj .databind .schema .Schema .JavaField ;
14
13
import tech .ydb .yoj .util .lang .Annotations ;
15
14
19
18
20
19
import static java .lang .reflect .Modifier .isAbstract ;
21
20
import static java .lang .reflect .Modifier .isStatic ;
22
- import static tech .ydb .yoj .databind .FieldValueType .STRING ;
23
21
24
22
@ ExperimentalApi (issue = "https://github.com/ydb-platform/yoj-project/issues/24" )
25
23
public final class CustomValueTypes {
26
- private static final Logger log = LoggerFactory .getLogger (CustomValueTypes .class );
27
-
28
24
private CustomValueTypes () {
29
25
}
30
26
31
27
public static Object preconvert (@ NonNull JavaField field , @ NonNull Object value ) {
32
- var cvt = field .getCustomValueType ();
33
- if (cvt != null ) {
34
- if (cvt .columnClass ().isInstance (value )) {
35
- // Value is already preconverted
36
- return value ;
37
- }
38
-
39
- value = createCustomValueTypeConverter (cvt ).toColumn (field , value );
40
-
41
- Preconditions .checkArgument (cvt .columnClass ().isInstance (value ),
42
- "Custom value type converter %s must produce a non-null value of type columnClass()=%s but got value of type %s" ,
43
- cvt .converter ().getCanonicalName (), cvt .columnClass ().getCanonicalName (), value .getClass ().getCanonicalName ());
44
- } else {
45
- // Legacy string-valued type (registered by FieldValueType.registerStringValueType())
46
- if (field .getValueType () == STRING && !String .class .equals (field .getRawType ()) && !(value instanceof String )) {
47
- log .warn ("You are using FieldValueType.registerStringValueType({}.class) which is deprecated for removal. "
48
- + "Please use @StringColumn annotation on the Entity field or a @StringValueType annotation on the string-valued type" ,
49
- field .getRawType ().getCanonicalName ());
50
- return new StringValueConverter <>().toColumn (field , value );
51
- }
28
+ var cvt = field .getCustomValueTypeInfo ();
29
+ if (cvt == null ) {
30
+ return value ;
31
+ }
32
+ if (cvt .getColumnClass ().isInstance (value )) {
33
+ // Value is already preconverted
34
+ return value ;
52
35
}
36
+
37
+ value = cvt .toColumn (field , value );
38
+ Preconditions .checkArgument (cvt .getColumnClass ().isInstance (value ),
39
+ "Custom value type converter %s must produce a non-null value of type columnClass()=%s but got value of type %s" ,
40
+ cvt .getConverter ().getClass ().getCanonicalName (),
41
+ cvt .getColumnClass ().getCanonicalName (),
42
+ value .getClass ().getCanonicalName ());
53
43
return value ;
54
44
}
55
45
56
- public static Object postconvert (@ NonNull JavaField field , @ NonNull Object value ) {
57
- var cvt = field .getCustomValueType ();
58
- if (cvt != null ) {
59
- value = createCustomValueTypeConverter (cvt ).toJava (field , value );
60
- } else {
61
- // Legacy string-valued type (registered by FieldValueType.registerStringValueType())
62
- if (field .getValueType () == STRING && !String .class .equals (field .getRawType ()) && value instanceof String ) {
63
- log .warn ("You are using FieldValueType.registerStringValueType({}.class) which is deprecated for removal. "
64
- + "Please use @StringColumn annotation on the Entity field or a @StringValueType annotation on the string-valued type" ,
65
- field .getRawType ().getCanonicalName ());
66
- return new StringValueConverter <>().toJava (field , (String ) value );
67
- }
46
+ public static <C extends Comparable <? super C >> Object postconvert (@ NonNull JavaField field , @ NonNull Object value ) {
47
+ CustomValueTypeInfo <?, C > cvt = field .getCustomValueTypeInfo ();
48
+ if (cvt == null ) {
49
+ return value ;
68
50
}
69
- return value ;
51
+
52
+ Preconditions .checkArgument (value instanceof Comparable , "postconvert() only takes Comparable values, but got value of %s" , value .getClass ());
53
+
54
+ @ SuppressWarnings ("unchecked" ) C comparable = (C ) value ;
55
+ return cvt .toJava (field , comparable );
70
56
}
71
57
72
- // TODO: Add caching to e.g. SchemaRegistry using @CustomValueType+[optionally JavaField if there is @Column annotation]+[type] as key,
73
- // to avoid repetitive construction of ValueConverters
74
- private static <V , C > ValueConverter <V , C > createCustomValueTypeConverter (CustomValueType cvt ) {
58
+ private static <J , C extends Comparable <? super C >> ValueConverter <J , C > createCustomValueTypeConverter (CustomValueType cvt ) {
75
59
try {
76
60
var ctor = cvt .converter ().getDeclaredConstructor ();
77
61
ctor .setAccessible (true );
78
- @ SuppressWarnings ("unchecked" ) var converter = (ValueConverter <V , C >) ctor .newInstance ();
62
+ @ SuppressWarnings ("unchecked" ) var converter = (ValueConverter <J , C >) ctor .newInstance ();
79
63
return converter ;
80
64
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | InvocationTargetException e ) {
81
65
throw new CustomConverterException (e , "Could not return custom value type converter " + cvt .converter ());
@@ -84,9 +68,32 @@ private static <V, C> ValueConverter<V, C> createCustomValueTypeConverter(Custom
84
68
85
69
@ Nullable
86
70
@ ExperimentalApi (issue = "https://github.com/ydb-platform/yoj-project/issues/24" )
87
- public static CustomValueType getCustomValueType (@ NonNull Type type , @ Nullable Column columnAnnotation ) {
88
- var rawType = type instanceof Class <?> ? (Class <?>) type : TypeToken .of (type ).getRawType ();
71
+ public static <J , C extends Comparable <? super C >> CustomValueTypeInfo <J , C > getCustomValueTypeInfo (
72
+ @ NonNull Type type , @ Nullable Column columnAnnotation
73
+ ) {
74
+ Class <?> rawType = type instanceof Class <?> ? (Class <?>) type : TypeToken .of (type ).getRawType ();
75
+ CustomValueType cvt = getCustomValueType (rawType , columnAnnotation );
76
+ if (cvt == null ) {
77
+ if (FieldValueType .isCustomStringValueType (rawType )) {
78
+ @ SuppressWarnings ("unchecked" )
79
+ var legacyStringVtInfo = (CustomValueTypeInfo <J , C >) new CustomValueTypeInfo <>(String .class , new StringValueConverter <J >());
80
+
81
+ return legacyStringVtInfo ;
82
+ }
83
+
84
+ return null ;
85
+ }
86
+
87
+ @ SuppressWarnings ("unchecked" )
88
+ Class <C > columnClass = (Class <C >) cvt .columnClass ();
89
89
90
+ ValueConverter <J , C > converter = createCustomValueTypeConverter (cvt );
91
+
92
+ return new CustomValueTypeInfo <>(columnClass , converter );
93
+ }
94
+
95
+ @ Nullable
96
+ private static CustomValueType getCustomValueType (@ NonNull Class <?> rawType , @ Nullable Column columnAnnotation ) {
90
97
var cvtAnnotation = columnAnnotation == null ? null : columnAnnotation .customValueType ();
91
98
92
99
var columnCvt = cvtAnnotation == null || cvtAnnotation .converter ().equals (ValueConverter .NoConverter .class ) ? null : cvtAnnotation ;
@@ -103,10 +110,12 @@ public static CustomValueType getCustomValueType(@NonNull Type type, @Nullable C
103
110
Preconditions .checkArgument (!columnClass .isInterface () && !isAbstract (columnClass .getModifiers ()),
104
111
"@CustomValueType.columnClass=%s must not be an interface or an abstract class" , columnClass .getCanonicalName ());
105
112
106
- var fvt = FieldValueType .forJavaType (columnClass , null );
113
+ var fvt = FieldValueType .forJavaType (columnClass );
107
114
Preconditions .checkArgument (!fvt .isComposite (),
108
115
"@CustomValueType.columnClass=%s must not map to FieldValueType.COMPOSITE" , columnClass .getCanonicalName ());
109
- Preconditions .checkArgument (!fvt .isUnknown (),
116
+
117
+ // TODO(entropia@): This won't be necessary when we remove FieldValueType.UNKNOWN in YOJ 3.0.0
118
+ Preconditions .checkArgument (fvt != FieldValueType .UNKNOWN ,
110
119
"@CustomValueType.columnClass=%s must not map to FieldValueType.UNKNOWN" , columnClass .getCanonicalName ());
111
120
112
121
var converterClass = cvt .converter ();
0 commit comments