1
+ package org .openapijsonschematools .schemas ;
2
+
3
+ import org .openapijsonschematools .configurations .JsonSchemaKeywordFlags ;
4
+ import org .openapijsonschematools .configurations .SchemaConfiguration ;
5
+
6
+ import java .lang .reflect .InvocationTargetException ;
7
+ import java .lang .reflect .Method ;
8
+ import java .math .BigDecimal ;
9
+ import java .time .LocalDate ;
10
+ import java .time .ZonedDateTime ;
11
+ import java .util .ArrayList ;
12
+ import java .util .HashSet ;
13
+ import java .util .LinkedHashMap ;
14
+ import java .util .LinkedHashSet ;
15
+ import java .util .List ;
16
+ import java .util .Map ;
17
+ import java .util .Objects ;
18
+ import java .util .Set ;
19
+
20
+ public interface Schema <T extends Map , U extends List > extends SchemaValidator {
21
+ private static Object castToAllowedTypes (Object arg , List <Object > pathToItem , PathToTypeMap pathToType ) {
22
+ if (arg == null ) {
23
+ pathToType .put (pathToItem , Void .class );
24
+ return null ;
25
+ } else if (arg instanceof String ) {
26
+ pathToType .put (pathToItem , String .class );
27
+ return arg ;
28
+ } else if (arg instanceof Map ) {
29
+ pathToType .put (pathToItem , Map .class );
30
+ LinkedHashMap <String , Object > argFixed = new LinkedHashMap <>();
31
+ for (Map .Entry <?, ?> entry : ((Map <?, ?>) arg ).entrySet ()) {
32
+ String key = (String ) entry .getKey ();
33
+ Object val = entry .getValue ();
34
+ List <Object > newPathToItem = new ArrayList <>(pathToItem );
35
+ newPathToItem .add (key );
36
+ Object fixedVal = castToAllowedTypes (val , newPathToItem , pathToType );
37
+ argFixed .put (key , fixedVal );
38
+ }
39
+ return argFixed ;
40
+ } else if (arg instanceof Boolean ) {
41
+ pathToType .put (pathToItem , Boolean .class );
42
+ return arg ;
43
+ } else if (arg instanceof Integer ) {
44
+ pathToType .put (pathToItem , Integer .class );
45
+ return arg ;
46
+ } else if (arg instanceof Float ) {
47
+ pathToType .put (pathToItem , Float .class );
48
+ return arg ;
49
+ } else if (arg instanceof Double ) {
50
+ pathToType .put (pathToItem , Double .class );
51
+ return arg ;
52
+ } else if (arg instanceof BigDecimal ) {
53
+ pathToType .put (pathToItem , BigDecimal .class );
54
+ return arg ;
55
+ } else if (arg instanceof List ) {
56
+ pathToType .put (pathToItem , List .class );
57
+ List <Object > argFixed = new ArrayList <>();
58
+ int i =0 ;
59
+ for (Object item : ((List <?>) arg ).toArray ()) {
60
+ List <Object > newPathToItem = new ArrayList <>(pathToItem );
61
+ newPathToItem .add (i );
62
+ Object fixedVal = castToAllowedTypes (item , newPathToItem , pathToType );
63
+ argFixed .add (fixedVal );
64
+ i += 1 ;
65
+ }
66
+ return argFixed ;
67
+ } else if (arg instanceof ZonedDateTime ) {
68
+ pathToType .put (pathToItem , String .class );
69
+ return arg .toString ();
70
+ } else if (arg instanceof LocalDate ) {
71
+ pathToType .put (pathToItem , String .class );
72
+ return arg .toString ();
73
+ } else {
74
+ Class <?> argClass = arg .getClass ();
75
+ throw new RuntimeException ("Invalid type passed in got input=" +arg +" type=" +argClass );
76
+ }
77
+ }
78
+
79
+ private static PathToSchemasMap getPathToSchemas (Class <Schema > cls , Object arg , ValidationMetadata validationMetadata , PathToTypeMap pathToType ) {
80
+ PathToSchemasMap pathToSchemasMap = new PathToSchemasMap ();
81
+ if (validationMetadata .validationRanEarlier (cls )) {
82
+ // todo add deeper validated schemas
83
+ } else {
84
+ PathToSchemasMap otherPathToSchemas = SchemaValidator .validate (cls , arg , validationMetadata );
85
+ pathToSchemasMap .update (otherPathToSchemas );
86
+ for (LinkedHashMap <Class <?>, Void > schemas : pathToSchemasMap .values ()) {
87
+ Class <?> firstSchema = schemas .entrySet ().iterator ().next ().getKey ();
88
+ schemas .clear ();
89
+ schemas .put (firstSchema , null );
90
+ }
91
+ Set <List <Object >> missingPaths = new HashSet <>(pathToType .keySet ());
92
+ missingPaths .removeAll (pathToSchemasMap .keySet ());
93
+ if (!missingPaths .isEmpty ()) {
94
+ LinkedHashMap <Class <?>, Void > unsetAnyTypeSchema = new LinkedHashMap <>();
95
+ unsetAnyTypeSchema .put (UnsetAnyTypeSchema .class , null );
96
+ for (List <Object > pathToItem : missingPaths ) {
97
+ pathToSchemasMap .put (pathToItem , unsetAnyTypeSchema );
98
+ }
99
+ }
100
+ }
101
+ return pathToSchemasMap ;
102
+ }
103
+
104
+ private static LinkedHashMap <String , Object > getProperties (Object arg , List <Object > pathToItem , PathToSchemasMap pathToSchemas ) {
105
+ LinkedHashMap <String , Object > properties = new LinkedHashMap <>();
106
+ Map <String , Object > castArg = (Map <String , Object >) arg ;
107
+ for (Map .Entry <String , Object > entry : castArg .entrySet ()) {
108
+ String propertyName = entry .getKey ();
109
+ List <Object > propertyPathToItem = new ArrayList <>(pathToItem );
110
+ propertyPathToItem .add (propertyName );
111
+ Class <Schema > propertyClass = (Class <Schema >) pathToSchemas .get (propertyPathToItem ).entrySet ().iterator ().next ().getKey ();
112
+ Object value = entry .getValue ();
113
+ Object castValue = getNewInstance (propertyClass , value , propertyPathToItem , pathToSchemas );
114
+ properties .put (propertyName , castValue );
115
+ }
116
+ return properties ;
117
+ }
118
+
119
+ private static List <Object > getItems (Object arg , List <Object > pathToItem , PathToSchemasMap pathToSchemas ) {
120
+ ArrayList <Object > items = new ArrayList <>();
121
+ List <Object > castItems = (List <Object >) arg ;
122
+ int i = 0 ;
123
+ for (Object item : castItems ) {
124
+ List <Object > itemPathToItem = new ArrayList <>(pathToItem );
125
+ itemPathToItem .add (i );
126
+ Class <Schema > itemClass = (Class <Schema >) pathToSchemas .get (itemPathToItem ).entrySet ().iterator ().next ().getKey ();
127
+ Object castItem = getNewInstance (itemClass , item , itemPathToItem , pathToSchemas );
128
+ items .add (castItem );
129
+ i += 1 ;
130
+ }
131
+ return items ;
132
+ }
133
+
134
+ private static Map <Class <?>, Class <?>> getTypeToOutputClass (Class <?> cls ) {
135
+ try {
136
+ // This must be implemented in Schemas that are generics as a static method
137
+ Method method = cls .getMethod ("typeToOutputClass" );
138
+ Map <Class <?>, Class <?>> typeToOutputClass = (Map <Class <?>, Class <?>>) method .invoke (null );
139
+ return typeToOutputClass ;
140
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e ) {
141
+ return null ;
142
+ }
143
+ }
144
+
145
+ private static Object getNewInstance (Class <Schema > cls , Object arg , List <Object > pathToItem , PathToSchemasMap pathToSchemas ) {
146
+ Object usedArg ;
147
+ if (arg instanceof Map ) {
148
+ usedArg = getProperties (arg , pathToItem , pathToSchemas );
149
+ } else if (arg instanceof List ) {
150
+ usedArg = getItems (arg , pathToItem , pathToSchemas );
151
+ } else {
152
+ // str, int, float, boolean, null, FileIO, bytes
153
+ return arg ;
154
+ }
155
+ Class <?> argType = arg .getClass ();
156
+ Map <Class <?>, Class <?>> typeToOutputClass = getTypeToOutputClass (cls );
157
+ if (typeToOutputClass == null ) {
158
+ return usedArg ;
159
+ }
160
+ Class <?> outputClass = typeToOutputClass .get (argType );
161
+ // TODO add class instantiation here
162
+ return null ;
163
+ }
164
+
165
+ static Void validate (Class <?> cls , Void arg , SchemaConfiguration configuration ) {
166
+ return (Void ) validateObject (cls , arg , configuration );
167
+ }
168
+
169
+ static Boolean validate (Class <?> cls , Boolean arg , SchemaConfiguration configuration ) {
170
+ return (Boolean ) validateObject (cls , arg , configuration );
171
+ }
172
+
173
+ static Integer validate (Class <?> cls , Integer arg , SchemaConfiguration configuration ) {
174
+ return (Integer ) validateObject (cls , arg , configuration );
175
+ }
176
+
177
+ static Float validate (Class <?> cls , Float arg , SchemaConfiguration configuration ) {
178
+ return (Float ) validateObject (cls , arg , configuration );
179
+ }
180
+
181
+ static Double validate (Class <?> cls , Double arg , SchemaConfiguration configuration ) {
182
+ return (Double ) validateObject (cls , arg , configuration );
183
+ }
184
+
185
+ static String validate (Class <?> cls , String arg , SchemaConfiguration configuration ) {
186
+ return (String ) validateObject (cls , arg , configuration );
187
+ }
188
+
189
+ static String validate (Class <?> cls , ZonedDateTime arg , SchemaConfiguration configuration ) {
190
+ return (String ) validateObject (cls , arg , configuration );
191
+ }
192
+
193
+ static String validate (Class <?> cls , LocalDate arg , SchemaConfiguration configuration ) {
194
+ return (String ) validateObject (cls , arg , configuration );
195
+ }
196
+
197
+ static <T extends Map > T validate (Class <?> cls , T arg , SchemaConfiguration configuration ) {
198
+ return (T ) validateObject (cls , arg , configuration );
199
+ }
200
+
201
+ static <U extends List > U validate (Class <?> cls , U arg , SchemaConfiguration configuration ) {
202
+ return (U ) validateObject (cls , arg , configuration );
203
+ }
204
+
205
+ // todo add bytes and FileIO
206
+
207
+ private static Object validateObject (Class <?> cls , Object arg , SchemaConfiguration configuration ) {
208
+ Class <Schema > castCls = (Class <Schema >) cls ;
209
+ if (arg instanceof Map || arg instanceof List ) {
210
+ // todo don't run validation if the instance is one of the class generic types
211
+ }
212
+ PathToTypeMap pathToType = new PathToTypeMap ();
213
+ List <Object > pathToItem = new ArrayList <>();
214
+ pathToItem .add ("args[0]" );
215
+ Object castArg = castToAllowedTypes (arg , pathToItem , pathToType );
216
+ SchemaConfiguration usedConfiguration = Objects .requireNonNullElseGet (configuration , () -> new SchemaConfiguration (JsonSchemaKeywordFlags .ofNone ()));
217
+ PathToSchemasMap validatedPathToSchemas = new PathToSchemasMap ();
218
+ ValidationMetadata validationMetadata = new ValidationMetadata (
219
+ pathToItem ,
220
+ usedConfiguration ,
221
+ validatedPathToSchemas ,
222
+ new LinkedHashSet <>()
223
+ );
224
+ PathToSchemasMap pathToSchemasMap = getPathToSchemas (castCls , castArg , validationMetadata , pathToType );
225
+ return getNewInstance (castCls , castArg , validationMetadata .pathToItem (), pathToSchemasMap );
226
+ }
227
+
228
+ }
0 commit comments