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