@@ -81,4 +81,127 @@ public String getPropertyName() {
8181 public Map <String , String > getMapping () {
8282 return mapping ;
8383 }
84+
85+ /**
86+ * Checks based on the current {@link DiscriminatorContext} whether the provided {@link JsonSchema} a match against
87+ * the current discriminator.
88+ *
89+ * @param currentDiscriminatorContext the currently active {@link DiscriminatorContext}
90+ * @param discriminator the discriminator to use for the check
91+ * @param discriminatorPropertyValue the value of the <code>discriminator/propertyName</code> field
92+ * @param jsonSchema the {@link JsonSchema} to check
93+ */
94+ public static void checkDiscriminatorMatch (final DiscriminatorContext currentDiscriminatorContext ,
95+ final ObjectNode discriminator ,
96+ final String discriminatorPropertyValue ,
97+ final JsonSchema jsonSchema ) {
98+ if (discriminatorPropertyValue == null ) {
99+ currentDiscriminatorContext .markIgnore ();
100+ return ;
101+ }
102+
103+ final JsonNode discriminatorMapping = discriminator .get ("mapping" );
104+ if (null == discriminatorMapping ) {
105+ checkForImplicitDiscriminatorMappingMatch (currentDiscriminatorContext ,
106+ discriminatorPropertyValue ,
107+ jsonSchema );
108+ } else {
109+ checkForExplicitDiscriminatorMappingMatch (currentDiscriminatorContext ,
110+ discriminatorPropertyValue ,
111+ discriminatorMapping ,
112+ jsonSchema );
113+ if (!currentDiscriminatorContext .isDiscriminatorMatchFound ()
114+ && noExplicitDiscriminatorKeyOverride (discriminatorMapping , jsonSchema )) {
115+ checkForImplicitDiscriminatorMappingMatch (currentDiscriminatorContext ,
116+ discriminatorPropertyValue ,
117+ jsonSchema );
118+ }
119+ }
120+ }
121+
122+ /**
123+ * Rolls up all nested and compatible discriminators to the root discriminator of the type. Detects attempts to redefine
124+ * the <code>propertyName</code> or mappings.
125+ *
126+ * @param currentDiscriminatorContext the currently active {@link DiscriminatorContext}
127+ * @param discriminator the discriminator to use for the check
128+ * @param schema the value of the <code>discriminator/propertyName</code> field
129+ * @param instanceLocation the logging prefix
130+ */
131+ public static void registerAndMergeDiscriminator (final DiscriminatorContext currentDiscriminatorContext ,
132+ final ObjectNode discriminator ,
133+ final JsonSchema schema ,
134+ final JsonNodePath instanceLocation ) {
135+ final JsonNode discriminatorOnSchema = schema .schemaNode .get ("discriminator" );
136+ if (null != discriminatorOnSchema && null != currentDiscriminatorContext
137+ .getDiscriminatorForPath (schema .schemaLocation )) {
138+ // this is where A -> B -> C inheritance exists, A has the root discriminator and B adds to the mapping
139+ final JsonNode propertyName = discriminatorOnSchema .get ("propertyName" );
140+ if (null != propertyName ) {
141+ throw new JsonSchemaException (instanceLocation + " schema " + schema + " attempts redefining the discriminator property" );
142+ }
143+ final ObjectNode mappingOnContextDiscriminator = (ObjectNode ) discriminator .get ("mapping" );
144+ final ObjectNode mappingOnCurrentSchemaDiscriminator = (ObjectNode ) discriminatorOnSchema .get ("mapping" );
145+ if (null == mappingOnContextDiscriminator && null != mappingOnCurrentSchemaDiscriminator ) {
146+ // here we have a mapping on a nested discriminator and none on the root discriminator, so we can simply
147+ // make it the root's
148+ discriminator .set ("mapping" , discriminatorOnSchema );
149+ } else if (null != mappingOnContextDiscriminator && null != mappingOnCurrentSchemaDiscriminator ) {
150+ // here we have to merge. The spec doesn't specify anything on this, but here we don't accept redefinition of
151+ // mappings that already exist
152+ final Iterator <Map .Entry <String , JsonNode >> fieldsToAdd = mappingOnCurrentSchemaDiscriminator .fields ();
153+ while (fieldsToAdd .hasNext ()) {
154+ final Map .Entry <String , JsonNode > fieldToAdd = fieldsToAdd .next ();
155+ final String mappingKeyToAdd = fieldToAdd .getKey ();
156+ final JsonNode mappingValueToAdd = fieldToAdd .getValue ();
157+
158+ final JsonNode currentMappingValue = mappingOnContextDiscriminator .get (mappingKeyToAdd );
159+ if (null != currentMappingValue && currentMappingValue != mappingValueToAdd ) {
160+ throw new JsonSchemaException (instanceLocation + "discriminator mapping redefinition from " + mappingKeyToAdd
161+ + "/" + currentMappingValue + " to " + mappingValueToAdd );
162+ } else if (null == currentMappingValue ) {
163+ mappingOnContextDiscriminator .set (mappingKeyToAdd , mappingValueToAdd );
164+ }
165+ }
166+ }
167+ }
168+ currentDiscriminatorContext .registerDiscriminator (schema .schemaLocation , discriminator );
169+ }
170+
171+ private static void checkForImplicitDiscriminatorMappingMatch (final DiscriminatorContext currentDiscriminatorContext ,
172+ final String discriminatorPropertyValue ,
173+ final JsonSchema schema ) {
174+ if (schema .schemaLocation .getFragment ().getName (-1 ).equals (discriminatorPropertyValue )) {
175+ currentDiscriminatorContext .markMatch ();
176+ }
177+ }
178+
179+ private static void checkForExplicitDiscriminatorMappingMatch (final DiscriminatorContext currentDiscriminatorContext ,
180+ final String discriminatorPropertyValue ,
181+ final JsonNode discriminatorMapping ,
182+ final JsonSchema schema ) {
183+ final Iterator <Map .Entry <String , JsonNode >> explicitMappings = discriminatorMapping .fields ();
184+ while (explicitMappings .hasNext ()) {
185+ final Map .Entry <String , JsonNode > candidateExplicitMapping = explicitMappings .next ();
186+ if (candidateExplicitMapping .getKey ().equals (discriminatorPropertyValue )
187+ && ("#" + schema .schemaLocation .getFragment ().toString ())
188+ .equals (candidateExplicitMapping .getValue ().asText ())) {
189+ currentDiscriminatorContext .markMatch ();
190+ break ;
191+ }
192+ }
193+ }
194+
195+ private static boolean noExplicitDiscriminatorKeyOverride (final JsonNode discriminatorMapping ,
196+ final JsonSchema parentSchema ) {
197+ final Iterator <Map .Entry <String , JsonNode >> explicitMappings = discriminatorMapping .fields ();
198+ while (explicitMappings .hasNext ()) {
199+ final Map .Entry <String , JsonNode > candidateExplicitMapping = explicitMappings .next ();
200+ if (candidateExplicitMapping .getValue ().asText ()
201+ .equals (parentSchema .schemaLocation .getFragment ().toString ())) {
202+ return false ;
203+ }
204+ }
205+ return true ;
206+ }
84207}
0 commit comments