001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.jsonschema;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023
024import java.net.*;
025import java.util.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.annotation.*;
029import org.apache.juneau.commons.reflect.*;
030import org.apache.juneau.json.*;
031import org.apache.juneau.parser.*;
032import org.apache.juneau.serializer.*;
033import org.apache.juneau.swap.*;
034
035/**
036 * Represents a top-level schema object bean in the JSON-Schema core specification.
037 *
038 * <p>
039 * This implementation follows the JSON Schema Draft 2020-12 specification.
040 *
041 * <h5 class='section'>Example:</h5>
042 * <p class='bjava'>
043 *    <jc>// Create a simple schema for a person object</jc>
044 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
045 *       .setIdUri(<js>"https://example.com/person.schema.json"</js>)
046 *       .setSchemaVersionUri(<js>"https://json-schema.org/draft/2020-12/schema"</js>)
047 *       .setTitle(<js>"Person"</js>)
048 *       .setDescription(<js>"A person object"</js>)
049 *       .setType(JsonType.<jsf>OBJECT</jsf>)
050 *       .addProperties(
051 *          <jk>new</jk> JsonSchemaProperty(<js>"firstName"</js>, JsonType.<jsf>STRING</jsf>)
052 *             .setMinLength(1)
053 *             .setMaxLength(50),
054 *          <jk>new</jk> JsonSchemaProperty(<js>"lastName"</js>, JsonType.<jsf>STRING</jsf>)
055 *             .setMinLength(1)
056 *             .setMaxLength(50),
057 *          <jk>new</jk> JsonSchemaProperty(<js>"age"</js>, JsonType.<jsf>INTEGER</jsf>)
058 *             .setMinimum(0)
059 *             .setExclusiveMaximum(150)
060 *       )
061 *       .addRequired(<js>"firstName"</js>, <js>"lastName"</js>);
062 *
063 *    <jc>// Serialize to JSON Schema</jc>
064 *    String <jv>json</jv> = JsonSerializer.<jsf>DEFAULT_SORTED</jsf>.serialize(<jv>schema</jv>);
065 * </p>
066 *
067 * <p>
068 * Output:
069 * <p class='bjson'>
070 *    {
071 *       <js>"$id"</js>: <js>"https://example.com/person.schema.json"</js>,
072 *       <js>"$schema"</js>: <js>"https://json-schema.org/draft/2020-12/schema"</js>,
073 *       <js>"title"</js>: <js>"Person"</js>,
074 *       <js>"description"</js>: <js>"A person object"</js>,
075 *       <js>"type"</js>: <js>"object"</js>,
076 *       <js>"properties"</js>: {
077 *          <js>"firstName"</js>: {
078 *             <js>"type"</js>: <js>"string"</js>,
079 *             <js>"minLength"</js>: 1,
080 *             <js>"maxLength"</js>: 50
081 *          },
082 *          <js>"lastName"</js>: {
083 *             <js>"type"</js>: <js>"string"</js>,
084 *             <js>"minLength"</js>: 1,
085 *             <js>"maxLength"</js>: 50
086 *          },
087 *          <js>"age"</js>: {
088 *             <js>"type"</js>: <js>"integer"</js>,
089 *             <js>"minimum"</js>: 0,
090 *             <js>"exclusiveMaximum"</js>: 150
091 *          }
092 *       },
093 *       <js>"required"</js>: [<js>"firstName"</js>, <js>"lastName"</js>]
094 *    }
095 * </p>
096 *
097 * <h5 class='section'>Key Features:</h5>
098 * <ul class='spaced-list'>
099 *    <li><b>Draft 2020-12 Support:</b> Includes all properties from the latest JSON Schema specification
100 *    <li><b>Backward Compatibility:</b> Deprecated Draft 04 properties (like <c>id</c> and <c>definitions</c>) are still supported
101 *    <li><b>Fluent API:</b> All setter methods return <c>this</c> for method chaining
102 *    <li><b>Type Safety:</b> Uses enums and typed collections for validation
103 *    <li><b>Serialization:</b> Can be serialized to any format supported by Juneau (JSON, XML, HTML, etc.)
104 * </ul>
105 *
106 * <h5 class='section'>Common Use Cases:</h5>
107 *
108 * <p><b>1. Simple Type Constraints:</b>
109 * <p class='bjava'>
110 *    <jc>// String with length constraints</jc>
111 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
112 *       .setType(JsonType.<jsf>STRING</jsf>)
113 *       .setMinLength(5)
114 *       .setMaxLength(100)
115 *       .setPattern(<js>"^[A-Za-z]+$"</js>);
116 * </p>
117 *
118 * <p><b>2. Numeric Ranges:</b>
119 * <p class='bjava'>
120 *    <jc>// Number between 0 and 100 (exclusive)</jc>
121 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
122 *       .setType(JsonType.<jsf>NUMBER</jsf>)
123 *       .setExclusiveMinimum(0)
124 *       .setExclusiveMaximum(100)
125 *       .setMultipleOf(0.5);
126 * </p>
127 *
128 * <p><b>3. Enumerations:</b>
129 * <p class='bjava'>
130 *    <jc>// Status field with allowed values</jc>
131 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
132 *       .setType(JsonType.<jsf>STRING</jsf>)
133 *       .addEnum(<js>"pending"</js>, <js>"active"</js>, <js>"completed"</js>);
134 * </p>
135 *
136 * <p><b>4. Arrays:</b>
137 * <p class='bjava'>
138 *    <jc>// Array of strings with constraints</jc>
139 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
140 *       .setType(JsonType.<jsf>ARRAY</jsf>)
141 *       .setItems(<jk>new</jk> JsonSchema().setType(JsonType.<jsf>STRING</jsf>))
142 *       .setMinItems(1)
143 *       .setMaxItems(10)
144 *       .setUniqueItems(<jk>true</jk>);
145 * </p>
146 *
147 * <p><b>5. Conditional Schemas (Draft 07+):</b>
148 * <p class='bjava'>
149 *    <jc>// Different validation based on country</jc>
150 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
151 *       .setType(JsonType.<jsf>OBJECT</jsf>)
152 *       .addProperties(
153 *          <jk>new</jk> JsonSchemaProperty(<js>"country"</js>, JsonType.<jsf>STRING</jsf>),
154 *          <jk>new</jk> JsonSchemaProperty(<js>"postalCode"</js>, JsonType.<jsf>STRING</jsf>)
155 *       )
156 *       .setIf(<jk>new</jk> JsonSchema()
157 *          .addProperties(<jk>new</jk> JsonSchemaProperty(<js>"country"</js>).setConst(<js>"USA"</js>))
158 *       )
159 *       .setThen(<jk>new</jk> JsonSchema()
160 *          .addProperties(<jk>new</jk> JsonSchemaProperty(<js>"postalCode"</js>).setPattern(<js>"^[0-9]{5}$"</js>))
161 *       );
162 * </p>
163 *
164 * <p><b>6. Reusable Definitions:</b>
165 * <p class='bjava'>
166 *    <jc>// Schema with reusable components using $defs</jc>
167 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
168 *       .setType(JsonType.<jsf>OBJECT</jsf>)
169 *       .addDef(<js>"address"</js>, <jk>new</jk> JsonSchema()
170 *          .setType(JsonType.<jsf>OBJECT</jsf>)
171 *          .addProperties(
172 *             <jk>new</jk> JsonSchemaProperty(<js>"street"</js>, JsonType.<jsf>STRING</jsf>),
173 *             <jk>new</jk> JsonSchemaProperty(<js>"city"</js>, JsonType.<jsf>STRING</jsf>)
174 *          )
175 *       )
176 *       .addProperties(
177 *          <jk>new</jk> JsonSchemaProperty(<js>"billingAddress"</js>)
178 *             .setRef(<js>"#/$defs/address"</js>),
179 *          <jk>new</jk> JsonSchemaProperty(<js>"shippingAddress"</js>)
180 *             .setRef(<js>"#/$defs/address"</js>)
181 *       );
182 * </p>
183 *
184 * <h5 class='section'>Migration from Draft 04:</h5>
185 * <ul class='spaced-list'>
186 *    <li>Use {@link #setIdUri(Object)} instead of {@link #setId(Object)} (deprecated)
187 *    <li>Use {@link #setDefs(Map)} instead of {@link #setDefinitions(Map)} (deprecated but still works)
188 *    <li>Use {@link #setExclusiveMaximum(Number)} with a numeric value instead of a boolean flag
189 *    <li>Use {@link #setExclusiveMinimum(Number)} with a numeric value instead of a boolean flag
190 * </ul>
191 *
192 * <h5 class='section'>See Also:</h5><ul>
193 *    <li class='link'><a href="https://json-schema.org/draft/2020-12/json-schema-core.html">JSON Schema 2020-12 Core</a>
194 *    <li class='link'><a href="https://json-schema.org/draft/2020-12/json-schema-validation.html">JSON Schema 2020-12 Validation</a>
195 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema">juneau-bean-jsonschema</a>
196 * </ul>
197 */
198@Bean(typeName = "schema")
199public class JsonSchema {
200   /**
201    * Used during parsing to convert the <property>additionalItems</property> property to the correct class type.
202    *
203    * <ul class='spaced-list'>
204    *    <li>
205    *       If parsing a JSON-array, converts to a {@link JsonSchemaArray}.
206    *    <li>
207    *       If parsing a JSON-boolean, converts to a {@link Boolean}.
208    * </ul>
209    *
210    * <p>
211    * Serialization method is a no-op.
212    */
213   public static class BooleanOrSchemaArraySwap extends ObjectSwap<Object,Object> {
214
215      @Override /* Overridden from ObjectSwap */
216      public Object swap(BeanSession session, Object o) throws SerializeException {
217         return o;
218      }
219
220      @Override /* Overridden from ObjectSwap */
221      public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException {
222         var cm = (o instanceof Collection ? session.getClassMeta(JsonSchemaArray.class) : session.getClassMeta(Boolean.class));
223         return session.convertToType(o, cm);
224      }
225   }
226
227   /**
228    * Used during parsing to convert the <property>additionalProperties</property> property to the correct class type.
229    *
230    * <ul class='spaced-list'>
231    *    <li>
232    *       If parsing a JSON-object, converts to a {@link JsonSchema}.
233    *    <li>
234    *       If parsing a JSON-boolean, converts to a {@link Boolean}.
235    * </ul>
236    *
237    * <p>
238    * Serialization method is a no-op.
239    */
240   public static class BooleanOrSchemaSwap extends ObjectSwap<Object,Object> {
241
242      @Override /* Overridden from ObjectSwap */
243      public Object swap(BeanSession session, Object o) throws SerializeException {
244         return o;
245      }
246
247      @Override /* Overridden from ObjectSwap */
248      public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException {
249         var cm = (o instanceof Boolean ? session.getClassMeta(Boolean.class) : session.getClassMeta(JsonSchema.class));
250         return session.convertToType(o, cm);
251      }
252   }
253
254   /**
255    * Used during parsing to convert the <property>items</property> property to the correct class type.
256    *
257    * <ul class='spaced-list'>
258    *    <li>
259    *       If parsing a JSON-array, converts to a {@link JsonSchemaArray}.
260    *    <li>
261    *       If parsing a JSON-object, converts to a {@link JsonSchema}.
262    * </ul>
263    *
264    * <p>
265    * Serialization method is a no-op.
266    */
267   public static class JsonSchemaOrSchemaArraySwap extends ObjectSwap<Object,Object> {
268
269      @Override /* Overridden from ObjectSwap */
270      public Object swap(BeanSession session, Object o) throws SerializeException {
271         return o;
272      }
273
274      @Override /* Overridden from ObjectSwap */
275      public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException {
276         var cm = (o instanceof Collection ? session.getClassMeta(JsonSchemaArray.class) : session.getClassMeta(JsonSchema.class));
277         return session.convertToType(o, cm);
278      }
279   }
280
281   /**
282    * Used during parsing to convert the <property>type</property> property to the correct class type.
283    *
284    * <ul class='spaced-list'>
285    *    <li>
286    *       If parsing a JSON-array, converts to a {@link JsonTypeArray}.
287    *    <li>
288    *       If parsing a JSON-object, converts to a {@link JsonType}.
289    * </ul>
290    *
291    * <p>
292    * Serialization method is a no-op.
293    */
294   public static class JsonTypeOrJsonTypeArraySwap extends ObjectSwap<Object,Object> {
295
296      @Override /* Overridden from ObjectSwap */
297      public Object swap(BeanSession session, Object o) throws SerializeException {
298         return o;
299      }
300
301      @Override /* Overridden from ObjectSwap */
302      public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException {
303         var cm = (o instanceof Collection ? session.getClassMeta(JsonTypeArray.class) : session.getClassMeta(JsonType.class));
304         return session.convertToType(o, cm);
305      }
306   }
307
308   // @formatter:off
309   private String name;                                   // Property name.  Not serialized.
310   private URI idUri;                                     // Draft 2020-12: $id
311   private URI id;                                        // Draft 04: id (deprecated but kept for compatibility)
312   private URI schemaVersion;
313   private String title;
314   private String description;
315   private JsonType typeJsonType;                         // JsonType representation of type
316   private JsonTypeArray typeJsonTypeArray;               // JsonTypeArray representation of type
317   private Map<String,JsonSchema> definitions;            // Retained for backward compatibility
318   private Map<String,JsonSchema> defs;                   // Draft 2020-12: $defs
319   private Map<String,JsonSchema> properties;
320   private Map<String,JsonSchema> patternProperties;
321   private Map<String,JsonSchema> dependencies;           // Retained for backward compatibility
322   private Map<String,JsonSchema> dependentSchemas;       // Draft 2019-09+
323   private Map<String,List<String>> dependentRequired;    // Draft 2019-09+
324   private JsonSchema itemsSchema;                        // JsonSchema representation of items
325   private JsonSchemaArray itemsSchemaArray;              // JsonSchemaArray representation of items
326   private JsonSchemaArray prefixItems;                   // Draft 2020-12: replaces tuple validation
327   private Number multipleOf;
328   private Number maximum;
329   private Number exclusiveMaximum;                       // Draft 06+: changed from Boolean to Number
330   private Number minimum;
331   private Number exclusiveMinimum;                       // Draft 06+: changed from Boolean to Number
332   private Integer maxLength;
333   private Integer minLength;
334   private String pattern;
335   private Boolean additionalItemsBoolean;                // Boolean representation of additionalItems
336   private JsonSchemaArray additionalItemsSchemaArray;    // JsonSchemaArray representation of additionalItems
337   private JsonSchema unevaluatedItems;                   // Draft 2019-09+
338   private Integer maxItems;
339   private Integer minItems;
340   private Boolean uniqueItems;
341   private Integer maxProperties;
342   private Integer minProperties;
343   private List<String> required;
344   private Boolean additionalPropertiesBoolean;           // Boolean representation of additionalProperties
345   private JsonSchema additionalPropertiesSchema;         // JsonSchema representation of additionalProperties
346   private JsonSchema unevaluatedProperties;              // Draft 2019-09+
347   private List<Object> enum_;                            // Changed to Object to support any type
348   private Object const_;                                 // Draft 06+
349   private List<Object> examples;                         // Draft 06+
350   private List<JsonSchema> allOf;
351   private List<JsonSchema> anyOf;
352   private List<JsonSchema> oneOf;
353   private JsonSchema not;
354   private JsonSchema if_;                                // Draft 07+
355   private JsonSchema then_;                              // Draft 07+
356   private JsonSchema else_;                              // Draft 07+
357   private Boolean readOnly;                              // Draft 07+
358   private Boolean writeOnly;                             // Draft 07+
359   private String contentMediaType;                       // Draft 07+
360
361   private String contentEncoding;                        // Draft 07+
362
363   private URI ref;
364
365   private JsonSchemaMap schemaMap;
366
367   private JsonSchema master = this;
368   // @formatter:on
369
370   /**
371    * Default constructor.
372    */
373   public JsonSchema() { /* Empty constructor. */ }
374
375   /**
376    * Bean property appender:  <property>additionalItems</property>.
377    *
378    * @param additionalItems
379    *    The list of items to append to the <property>additionalItems</property> property on this bean.
380    * @return This object.
381    */
382   public JsonSchema addAdditionalItems(JsonSchema...additionalItems) {
383      if (this.additionalItemsSchemaArray == null)
384         this.additionalItemsSchemaArray = new JsonSchemaArray();
385      this.additionalItemsSchemaArray.addAll(additionalItems);
386      setMasterOn(additionalItems);
387      return this;
388   }
389
390   /**
391    * Bean property appender:  <property>allOf</property>.
392    *
393    * @param allOf The list of items to append to the <property>allOf</property> property on this bean.
394    * @return This object.
395    */
396   public JsonSchema addAllOf(JsonSchema...allOf) {
397      setMasterOn(allOf);
398      this.allOf = addAll(this.allOf, allOf);
399      return this;
400   }
401
402   /**
403    * Bean property appender:  <property>anyOf</property>.
404    *
405    * @param anyOf The list of items to append to the <property>anyOf</property> property on this bean.
406    * @return This object.
407    */
408   public JsonSchema addAnyOf(JsonSchema...anyOf) {
409      if (this.anyOf == null)
410         this.anyOf = new LinkedList<>();
411      setMasterOn(anyOf);
412      for (var s : anyOf)
413         this.anyOf.add(s);
414      return this;
415   }
416
417   /**
418    * Bean property appender:  <property>$defs</property>.
419    *
420    * @param name The key in the defs map entry.
421    * @param def The value in the defs map entry.
422    * @return This object.
423    */
424   public JsonSchema addDef(String name, JsonSchema def) {
425      if (this.defs == null)
426         this.defs = map();
427      this.defs.put(name, def);
428      setMasterOn(def);
429      return this;
430   }
431
432   /**
433    * Bean property appender:  <property>definitions</property>.
434    *
435    * @param name The key in the definitions map entry.
436    * @param definition The value in the definitions map entry.
437    * @return This object.
438    */
439   public JsonSchema addDefinition(String name, JsonSchema definition) {
440      if (this.definitions == null)
441         this.definitions = map();
442      this.definitions.put(name, definition);
443      setMasterOn(definition);
444      return this;
445   }
446
447   /**
448    * Bean property appender:  <property>dependencies</property>.
449    *
450    * @param name The key of the entry in the dependencies map.
451    * @param dependency The value of the entry in the dependencies map.
452    * @return This object.
453    */
454   public JsonSchema addDependency(String name, JsonSchema dependency) {
455      if (this.dependencies == null)
456         this.dependencies = map();
457      this.dependencies.put(name, dependency);
458      setMasterOn(dependency);
459      return this;
460   }
461
462   /**
463    * Bean property appender:  <property>dependentRequired</property>.
464    *
465    * @param name The key of the entry in the dependentRequired map.
466    * @param required The value of the entry in the dependentRequired map.
467    * @return This object.
468    */
469   public JsonSchema addDependentRequired(String name, List<String> required) {
470      if (this.dependentRequired == null)
471         this.dependentRequired = map();
472      this.dependentRequired.put(name, required);
473      return this;
474   }
475
476   /**
477    * Bean property appender:  <property>dependentSchemas</property>.
478    *
479    * @param name The key of the entry in the dependentSchemas map.
480    * @param schema The value of the entry in the dependentSchemas map.
481    * @return This object.
482    */
483   public JsonSchema addDependentSchema(String name, JsonSchema schema) {
484      if (this.dependentSchemas == null)
485         this.dependentSchemas = map();
486      this.dependentSchemas.put(name, schema);
487      setMasterOn(schema);
488      return this;
489   }
490
491   /**
492    * Bean property appender:  <property>enum</property>.
493    *
494    * @param enum_ The list of items to append to the <property>enum</property> property on this bean.
495    * @return This object.
496    */
497   public JsonSchema addEnum(Object...enum_) {
498      if (this.enum_ == null)
499         this.enum_ = new LinkedList<>();
500      for (var e : enum_)
501         this.enum_.add(e);
502      return this;
503   }
504
505   /**
506    * Bean property appender:  <property>examples</property>.
507    *
508    * @param examples The list of items to append to the <property>examples</property> property on this bean.
509    * @return This object.
510    */
511   public JsonSchema addExamples(Object...examples) {
512      if (this.examples == null)
513         this.examples = new LinkedList<>();
514      for (var e : examples)
515         this.examples.add(e);
516      return this;
517   }
518
519   /**
520    * Bean property appender:  <property>items</property>.
521    *
522    * @param items The list of items to append to the <property>items</property> property on this bean.
523    * @return This object.
524    */
525   public JsonSchema addItems(JsonSchema...items) {
526      if (this.itemsSchemaArray == null)
527         this.itemsSchemaArray = new JsonSchemaArray();
528      this.itemsSchemaArray.addAll(items);
529      setMasterOn(items);
530      return this;
531   }
532
533   /**
534    * Bean property appender:  <property>oneOf</property>.
535    *
536    * @param oneOf The list of items to append to the <property>oneOf</property> property on this bean.
537    * @return This object.
538    */
539   public JsonSchema addOneOf(JsonSchema...oneOf) {
540      if (this.oneOf == null)
541         this.oneOf = new LinkedList<>();
542      setMasterOn(oneOf);
543      for (var s : oneOf)
544         this.oneOf.add(s);
545      return this;
546   }
547
548   /**
549    * Bean property appender:  <property>patternProperties</property>.
550    *
551    * <p>
552    * Properties must have their <property>name</property> property set to the pattern string when using this method.
553    *
554    * @param properties The list of items to append to the <property>patternProperties</property> property on this bean.
555    * @return This object.
556    * @throws BeanRuntimeException If property is found without a set <property>name</property> property.
557    */
558   public JsonSchema addPatternProperties(JsonSchemaProperty...properties) {
559      if (this.patternProperties == null)
560         this.patternProperties = map();
561      for (var p : properties) {
562         if (p.getName() == null)
563            throw bex(JsonSchema.class, "Invalid property passed to JsonSchema.addProperties().  Property name was null.");
564         setMasterOn(p);
565         this.patternProperties.put(p.getName(), p);
566      }
567      return this;
568   }
569
570   /**
571    * Bean property appender:  <property>prefixItems</property>.
572    *
573    * @param prefixItems The list of items to append to the <property>prefixItems</property> property on this bean.
574    * @return This object.
575    */
576   public JsonSchema addPrefixItems(JsonSchema...prefixItems) {
577      if (this.prefixItems == null)
578         this.prefixItems = new JsonSchemaArray();
579      this.prefixItems.addAll(prefixItems);
580      setMasterOn(prefixItems);
581      return this;
582   }
583
584   /**
585    * Bean property appender:  <property>properties</property>.
586    *
587    * <p>
588    * Properties must have their <property>name</property> property set on them when using this method.
589    *
590    * @param properties The list of items to append to the <property>properties</property> property on this bean.
591    * @return This object.
592    * @throws BeanRuntimeException If property is found without a set <property>name</property> property.
593    */
594   public JsonSchema addProperties(JsonSchema...properties) {
595      if (this.properties == null)
596         this.properties = map();
597      for (var p : properties) {
598         if (p.getName() == null)
599            throw bex(JsonSchema.class, "Invalid property passed to JsonSchema.addProperties().  Property name was null.");
600         setMasterOn(p);
601         this.properties.put(p.getName(), p);
602      }
603      return this;
604   }
605
606   /**
607    * Bean property appender:  <property>required</property>.
608    *
609    * @param properties The list of items to append to the <property>required</property> property on this bean.
610    * @return This object.
611    */
612   public JsonSchema addRequired(JsonSchemaProperty...properties) {
613      if (this.required == null)
614         this.required = new LinkedList<>();
615      for (var p : properties)
616         this.required.add(p.getName());
617      return this;
618   }
619
620   /**
621    * Bean property appender:  <property>required</property>.
622    *
623    * @param required The list of items to append to the <property>required</property> property on this bean.
624    * @return This object.
625    */
626   public JsonSchema addRequired(List<String> required) {
627      if (this.required == null)
628         this.required = new LinkedList<>();
629      required.forEach(x -> this.required.add(x));
630      return this;
631   }
632
633   /**
634    * Bean property appender:  <property>required</property>.
635    *
636    * @param required The list of items to append to the <property>required</property> property on this bean.
637    * @return This object.
638    */
639   public JsonSchema addRequired(String...required) {
640      if (this.required == null)
641         this.required = new LinkedList<>();
642      for (var r : required)
643         this.required.add(r);
644      return this;
645   }
646
647   /**
648    * Bean property appender:  <property>type</property>.
649    *
650    * @param types The list of items to append to the <property>type</property> property on this bean.
651    * @return This object.
652    */
653   public JsonSchema addTypes(JsonType...types) {
654      if (this.typeJsonTypeArray == null)
655         this.typeJsonTypeArray = new JsonTypeArray();
656      this.typeJsonTypeArray.addAll(types);
657      return this;
658   }
659
660   /**
661    * Bean property getter:  <property>additionalItems</property>.
662    *
663    * @return
664    *    The value of the <property>additionalItems</property> property on this bean, or <jk>null</jk> if it is
665    *    not set.
666    *    Can be either a {@link Boolean} or {@link JsonSchemaArray} depending on what value was used to set it.
667    */
668   @Swap(BooleanOrSchemaArraySwap.class)
669   public Object getAdditionalItems() {
670      if (nn(additionalItemsBoolean))
671         return additionalItemsBoolean;
672      return additionalItemsSchemaArray;
673   }
674
675   /**
676    * Bean property getter:  <property>additionalItems</property>.
677    *
678    * <p>
679    * Convenience method for returning the <property>additionalItems</property> property when it is a {@link Boolean}
680    * value.
681    *
682    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}.
683    */
684   @BeanIgnore
685   public Boolean getAdditionalItemsAsBoolean() { return additionalItemsBoolean; }
686
687   /**
688    * Bean property getter:  <property>additionalItems</property>.
689    *
690    * <p>
691    * Convenience method for returning the <property>additionalItems</property> property when it is a
692    * {@link JsonSchemaArray} value.
693    *
694    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}.
695    */
696   @BeanIgnore
697   public List<JsonSchema> getAdditionalItemsAsSchemaArray() { return additionalItemsSchemaArray; }
698
699   /**
700    * Bean property getter:  <property>additionalProperties</property>.
701    *
702    * @return
703    *    The value of the <property>additionalProperties</property> property on this bean, or <jk>null</jk> if it
704    *    is not set.
705    *    Can be either a {@link Boolean} or {@link JsonSchemaArray} depending on what value was used to set it.
706    */
707   @Swap(BooleanOrSchemaSwap.class)
708   public Object getAdditionalProperties() {
709      if (nn(additionalPropertiesBoolean))
710         return additionalItemsBoolean;
711      return additionalPropertiesSchema;
712   }
713
714   /**
715    * Bean property getter:  <property>additionalProperties</property>.
716    *
717    * <p>
718    * Convenience method for returning the <property>additionalProperties</property> property when it is a
719    * {@link Boolean} value.
720    *
721    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}.
722    */
723   @BeanIgnore
724   public Boolean getAdditionalPropertiesAsBoolean() { return additionalPropertiesBoolean; }
725
726   /**
727    * Bean property getter:  <property>additionalProperties</property>.
728    *
729    * <p>
730    * Convenience method for returning the <property>additionalProperties</property> property when it is a
731    * {@link JsonSchema} value.
732    *
733    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}.
734    */
735   @BeanIgnore
736   public JsonSchema getAdditionalPropertiesAsSchema() { return additionalPropertiesSchema; }
737
738   /**
739    * Bean property getter:  <property>allOf</property>.
740    *
741    * @return The value of the <property>allOf</property> property on this bean, or <jk>null</jk> if it is not set.
742    */
743   public List<JsonSchema> getAllOf() { return allOf; }
744
745   /**
746    * Bean property getter:  <property>anyOf</property>.
747    *
748    * @return The value of the <property>anyOf</property> property on this bean, or <jk>null</jk> if it is not set.
749    */
750   public List<JsonSchema> getAnyOf() { return anyOf; }
751
752   /**
753    * Bean property getter:  <property>const</property>.
754    *
755    * <p>
756    * This property was added in Draft 06.
757    *
758    * @return The value of the <property>const</property> property on this bean, or <jk>null</jk> if it is not set.
759    */
760   public Object getConst() { return const_; }
761
762   /**
763    * Bean property getter:  <property>contentEncoding</property>.
764    *
765    * <p>
766    * This property was added in Draft 07.
767    *
768    * @return The value of the <property>contentEncoding</property> property on this bean, or <jk>null</jk> if it is not set.
769    */
770   public String getContentEncoding() { return contentEncoding; }
771
772   /**
773    * Bean property getter:  <property>contentMediaType</property>.
774    *
775    * <p>
776    * This property was added in Draft 07.
777    *
778    * @return The value of the <property>contentMediaType</property> property on this bean, or <jk>null</jk> if it is not set.
779    */
780   public String getContentMediaType() { return contentMediaType; }
781
782   /**
783    * Bean property getter:  <property>definitions</property>.
784    *
785    * <p>
786    * <b>Deprecated:</b> Use {@link #getDefs()} for Draft 2020-12 compliance.
787    * This property is retained for Draft 04 backward compatibility.
788    *
789    * @return
790    *    The value of the <property>definitions</property> property on this bean, or <jk>null</jk> if it is not set.
791    */
792   public Map<String,JsonSchema> getDefinitions() {
793      return nn(definitions) ? definitions : defs; // Fall back to $defs for compatibility
794   }
795
796   /**
797    * Bean property getter:  <property>$defs</property>.
798    *
799    * <p>
800    * This is the Draft 2020-12 replacement for <property>definitions</property>.
801    * Both properties are supported for backward compatibility.
802    *
803    * @return The value of the <property>$defs</property> property on this bean, or <jk>null</jk> if it is not set.
804    */
805   @Beanp("$defs")
806   public Map<String,JsonSchema> getDefs() {
807      return defs; // Return only defs, not definitions (to avoid double serialization)
808   }
809
810   /**
811    * Bean property getter:  <property>dependencies</property>.
812    *
813    * @return
814    *    The value of the <property>dependencies</property> property on this bean, or <jk>null</jk> if it is not set.
815    */
816   public Map<String,JsonSchema> getDependencies() { return dependencies; }
817
818   /**
819    * Bean property getter:  <property>dependentRequired</property>.
820    *
821    * <p>
822    * This property was added in Draft 2019-09 as a replacement for the array form of <property>dependencies</property>.
823    *
824    * @return The value of the <property>dependentRequired</property> property on this bean, or <jk>null</jk> if it is not set.
825    */
826   public Map<String,List<String>> getDependentRequired() { return dependentRequired; }
827
828   /**
829    * Bean property getter:  <property>dependentSchemas</property>.
830    *
831    * <p>
832    * This property was added in Draft 2019-09 as a replacement for the schema form of <property>dependencies</property>.
833    *
834    * @return The value of the <property>dependentSchemas</property> property on this bean, or <jk>null</jk> if it is not set.
835    */
836   public Map<String,JsonSchema> getDependentSchemas() { return dependentSchemas; }
837
838   /**
839    * Bean property getter:  <property>description</property>.
840    *
841    * @return The value of the <property>description</property> property, or <jk>null</jk> if it is not set.
842    */
843   public String getDescription() { return description; }
844
845   /**
846    * Bean property getter:  <property>else</property>.
847    *
848    * <p>
849    * This property was added in Draft 07 for conditional schema application.
850    *
851    * @return The value of the <property>else</property> property on this bean, or <jk>null</jk> if it is not set.
852    */
853   @Beanp("else")
854   public JsonSchema getElse() { return else_; }
855
856   /**
857    * Bean property getter:  <property>enum</property>.
858    *
859    * @return The value of the <property>enum</property> property on this bean, or <jk>null</jk> if it is not set.
860    */
861   public List<Object> getEnum() { return enum_; }
862
863   /**
864    * Bean property getter:  <property>examples</property>.
865    *
866    * <p>
867    * This property was added in Draft 06.
868    *
869    * @return The value of the <property>examples</property> property on this bean, or <jk>null</jk> if it is not set.
870    */
871   public List<Object> getExamples() { return examples; }
872
873   /**
874    * Bean property getter:  <property>exclusiveMaximum</property>.
875    *
876    * <p>
877    * In Draft 06+, this is a numeric value representing the exclusive upper bound.
878    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
879    *
880    * @return
881    *    The value of the <property>exclusiveMaximum</property> property on this bean, or <jk>null</jk> if it is
882    *    not set.
883    */
884   public Number getExclusiveMaximum() { return exclusiveMaximum; }
885
886   /**
887    * Bean property getter:  <property>exclusiveMinimum</property>.
888    *
889    * <p>
890    * In Draft 06+, this is a numeric value representing the exclusive lower bound.
891    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
892    *
893    * @return
894    *    The value of the <property>exclusiveMinimum</property> property on this bean, or <jk>null</jk> if it is
895    *    not set.
896    */
897   public Number getExclusiveMinimum() { return exclusiveMinimum; }
898
899   /**
900    * Bean property getter:  <property>id</property>.
901    *
902    * <p>
903    * <b>Deprecated:</b> Use {@link #getIdUri()} instead.
904    * This property is retained for Draft 04 backward compatibility.
905    *
906    * @return The value of the <property>id</property> property on this bean, or <jk>null</jk> if it is not set.
907    * @deprecated Use {@link #getIdUri()} instead.
908    */
909   @Deprecated
910   public URI getId() {
911      return nn(id) ? id : idUri; // Fall back to new '$id' for compatibility when reading
912   }
913
914   /**
915    * Bean property getter:  <property>$id</property>.
916    *
917    * <p>
918    * This is the Draft 2020-12 property for schema identification.
919    *
920    * @return The value of the <property>$id</property> property on this bean, or <jk>null</jk> if it is not set.
921    */
922   @Beanp("$id")
923   public URI getIdUri() {
924      return idUri; // Return only idUri, not id (to avoid double serialization)
925   }
926
927   /**
928    * Bean property getter:  <property>if</property>.
929    *
930    * <p>
931    * This property was added in Draft 07 for conditional schema application.
932    *
933    * @return The value of the <property>if</property> property on this bean, or <jk>null</jk> if it is not set.
934    */
935   @Beanp("if")
936   public JsonSchema getIf() { return if_; }
937
938   /**
939    * Bean property getter:  <property>items</property>.
940    *
941    * @return
942    *    The value of the <property>items</property> property on this bean, or <jk>null</jk> if it is not set.
943    *    Can be either a {@link JsonSchema} or {@link JsonSchemaArray} depending on what value was used to set it.
944    */
945   @Swap(JsonSchemaOrSchemaArraySwap.class)
946   public Object getItems() {
947      if (nn(itemsSchema))
948         return itemsSchema;
949      return itemsSchemaArray;
950   }
951
952   /**
953    * Bean property getter:  <property>items</property>.
954    *
955    * <p>
956    * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchema} value.
957    *
958    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}.
959    */
960   @BeanIgnore
961   public JsonSchema getItemsAsSchema() { return itemsSchema; }
962
963   /**
964    * Bean property getter:  <property>items</property>.
965    *
966    * <p>
967    * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchemaArray} value.
968    *
969    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}.
970    */
971   @BeanIgnore
972   public JsonSchemaArray getItemsAsSchemaArray() { return itemsSchemaArray; }
973
974   /**
975    * Bean property getter:  <property>maximum</property>.
976    *
977    * @return The value of the <property>maximum</property> property on this bean, or <jk>null</jk> if it is not set.
978    */
979   public Number getMaximum() { return maximum; }
980
981   /**
982    * Bean property getter:  <property>maxItems</property>.
983    *
984    * @return The value of the <property>maxItems</property> property on this bean, or <jk>null</jk> if it is not set.
985    */
986   public Integer getMaxItems() { return maxItems; }
987
988   /**
989    * Bean property getter:  <property>maxLength</property>.
990    *
991    * @return The value of the <property>maxLength</property> property on this bean, or <jk>null</jk> if it is not set.
992    */
993   public Integer getMaxLength() { return maxLength; }
994
995   /**
996    * Bean property getter:  <property>maxProperties</property>.
997    *
998    * @return
999    *    The value of the <property>maxProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1000    */
1001   public Integer getMaxProperties() { return maxProperties; }
1002
1003   /**
1004    * Bean property getter:  <property>minimum</property>.
1005    *
1006    * @return The value of the <property>minimum</property> property on this bean, or <jk>null</jk> if it is not set.
1007    */
1008   public Number getMinimum() { return minimum; }
1009
1010   /**
1011    * Bean property getter:  <property>minItems</property>.
1012    *
1013    * @return The value of the <property>minItems</property> property on this bean, or <jk>null</jk> if it is not set.
1014    */
1015   public Integer getMinItems() { return minItems; }
1016
1017   /**
1018    * Bean property getter:  <property>minLength</property>.
1019    *
1020    * @return The value of the <property>minLength</property> property on this bean, or <jk>null</jk> if it is not set.
1021    */
1022   public Integer getMinLength() { return minLength; }
1023
1024   /**
1025    * Bean property getter:  <property>minProperties</property>.
1026    *
1027    * @return
1028    *    The value of the <property>minProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1029    */
1030   public Integer getMinProperties() { return minProperties; }
1031
1032   /**
1033    * Bean property getter:  <property>multipleOf</property>.
1034    *
1035    * @return The value of the <property>multipleOf</property> property on this bean, or <jk>null</jk> if it is not set.
1036    */
1037   public Number getMultipleOf() { return multipleOf; }
1038
1039   /**
1040    * Bean property getter:  <property>name</property>.
1041    *
1042    * <p>
1043    * This is an internal property used for tracking property names and is not part of the JSON Schema specification.
1044    *
1045    * @return The value of the <property>name</property> property on this bean, or <jk>null</jk> if it is not set.
1046    */
1047   @BeanIgnore
1048   public String getName() { return name; }
1049
1050   /**
1051    * Bean property getter:  <property>not</property>.
1052    *
1053    * @return The value of the <property>not</property> property on this bean, or <jk>null</jk> if it is not set.
1054    */
1055   public JsonSchema getNot() { return not; }
1056
1057   /**
1058    * Bean property getter:  <property>oneOf</property>.
1059    *
1060    * @return The value of the <property>oneOf</property> property on this bean, or <jk>null</jk> if it is not set.
1061    */
1062   public List<JsonSchema> getOneOf() { return oneOf; }
1063
1064   /**
1065    * Bean property getter:  <property>pattern</property>.
1066    *
1067    * @return The value of the <property>pattern</property> property on this bean, or <jk>null</jk> if it is not set.
1068    */
1069   public String getPattern() { return pattern; }
1070
1071   /**
1072    * Bean property getter:  <property>patternProperties</property>.
1073    *
1074    * @return
1075    *    The value of the <property>patternProperties</property> property on this bean, or <jk>null</jk> if it is
1076    *    not set.
1077    */
1078   public Map<String,JsonSchema> getPatternProperties() { return patternProperties; }
1079
1080   /**
1081    * Bean property getter:  <property>prefixItems</property>.
1082    *
1083    * <p>
1084    * This property was added in Draft 2020-12 for tuple validation.
1085    *
1086    * @return The value of the <property>prefixItems</property> property on this bean, or <jk>null</jk> if it is not set.
1087    */
1088   public JsonSchemaArray getPrefixItems() { return prefixItems; }
1089
1090   /**
1091    * Bean property getter:  <property>properties</property>.
1092    *
1093    * @return The value of the <property>properties</property> property on this bean, or <jk>null</jk> if it is not set.
1094    */
1095   public Map<String,JsonSchema> getProperties() { return properties; }
1096
1097   /**
1098    * Returns the property with the specified name.
1099    *
1100    * <p>
1101    * This is equivalent to calling <property>getProperty(name, <jk>false</jk>)</property>.
1102    *
1103    * @param name The property name.
1104    * @return The property with the specified name, or <jk>null</jk> if no property is specified.
1105    */
1106   public JsonSchema getProperty(String name) {
1107      return getProperty(name, false);
1108   }
1109
1110   /**
1111    * Returns the property with the specified name.
1112    *
1113    * <p>
1114    * If <property>resolve</property> is <jk>true</jk>, the property object will automatically be  resolved by calling
1115    * {@link #resolve()}.
1116    * Therefore, <property>getProperty(name, <jk>true</jk>)</property> is equivalent to calling
1117    * <property>getProperty(name).resolve()</property>, except it's safe from a potential
1118    * <property>NullPointerException</property>.
1119    *
1120    * @param name The property name.
1121    * @param resolve If <jk>true</jk>, calls {@link #resolve()} on object before returning.
1122    * @return The property with the specified name, or <jk>null</jk> if no property is specified.
1123    */
1124   public JsonSchema getProperty(String name, boolean resolve) {
1125      if (properties == null)
1126         return null;
1127      var s = properties.get(name);
1128      if (s == null)
1129         return null;
1130      if (resolve)
1131         s = s.resolve();
1132      return s;
1133   }
1134
1135   /**
1136    * Bean property getter:  <property>readOnly</property>.
1137    *
1138    * <p>
1139    * This property was added in Draft 07.
1140    *
1141    * @return The value of the <property>readOnly</property> property on this bean, or <jk>null</jk> if it is not set.
1142    */
1143   public Boolean getReadOnly() { return readOnly; }
1144
1145   /**
1146    * Bean property getter:  <property>$ref</property>.
1147    *
1148    * @return The value of the <property>$ref</property> property on this bean, or <jk>null</jk> if it is not set.
1149    */
1150   @Beanp("$ref")
1151   public URI getRef() { return ref; }
1152
1153   /**
1154    * Bean property getter:  <property>required</property>.
1155    *
1156    * @return The value of the <property>required</property> property on this bean, or <jk>null</jk> if it is not set.
1157    */
1158   public List<String> getRequired() { return required; }
1159
1160   /**
1161    * Bean property getter:  <property>$schema</property>.
1162    *
1163    * @return The value of the <property>$schema</property> property on this bean, or <jk>null</jk> if it is not set.
1164    */
1165   @Beanp("$schema")
1166   public URI getSchemaVersionUri() { return schemaVersion; }
1167
1168   /**
1169    * Bean property getter:  <property>then</property>.
1170    *
1171    * <p>
1172    * This property was added in Draft 07 for conditional schema application.
1173    *
1174    * @return The value of the <property>then</property> property on this bean, or <jk>null</jk> if it is not set.
1175    */
1176   @Beanp("then")
1177   public JsonSchema getThen() { return then_; }
1178
1179   /**
1180    * Bean property getter:  <property>title</property>.
1181    *
1182    * @return The value of the <property>title</property> property, or <jk>null</jk> if it is not set.
1183    */
1184   public String getTitle() { return title; }
1185
1186   /**
1187    * Bean property getter:  <property>type</property>.
1188    *
1189    * @return
1190    *    The value of the <property>type</property> property on this bean, or <jk>null</jk> if it is not set.
1191    *    Can be either a {@link JsonType} or {@link JsonTypeArray} depending on what value was used to set it.
1192    */
1193   @Swap(JsonTypeOrJsonTypeArraySwap.class)
1194   public Object getType() {
1195      if (nn(typeJsonType))
1196         return typeJsonType;
1197      return typeJsonTypeArray;
1198   }
1199
1200   /**
1201    * Bean property getter:  <property>type</property>.
1202    *
1203    * <p>
1204    * Convenience method for returning the <property>type</property> property when it is a {@link JsonType} value.
1205    *
1206    * @return
1207    *    The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonTypeArray}.
1208    */
1209   @BeanIgnore
1210   public JsonType getTypeAsJsonType() { return typeJsonType; }
1211
1212   /**
1213    * Bean property getter:  <property>type</property>.
1214    *
1215    * <p>
1216    * Convenience method for returning the <property>type</property> property when it is a {@link JsonTypeArray} value.
1217    *
1218    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonType}.
1219    */
1220   @BeanIgnore
1221   public JsonTypeArray getTypeAsJsonTypeArray() { return typeJsonTypeArray; }
1222
1223   /**
1224    * Bean property getter:  <property>unevaluatedItems</property>.
1225    *
1226    * <p>
1227    * This property was added in Draft 2019-09.
1228    *
1229    * @return The value of the <property>unevaluatedItems</property> property on this bean, or <jk>null</jk> if it is not set.
1230    */
1231   public JsonSchema getUnevaluatedItems() { return unevaluatedItems; }
1232
1233   /**
1234    * Bean property getter:  <property>unevaluatedProperties</property>.
1235    *
1236    * <p>
1237    * This property was added in Draft 2019-09.
1238    *
1239    * @return The value of the <property>unevaluatedProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1240    */
1241   public JsonSchema getUnevaluatedProperties() { return unevaluatedProperties; }
1242
1243   /**
1244    * Bean property getter:  <property>uniqueItems</property>.
1245    *
1246    * @return
1247    *    The value of the <property>uniqueItems</property> property on this bean, or <jk>null</jk> if it is not set.
1248    */
1249   public Boolean getUniqueItems() { return uniqueItems; }
1250
1251   /**
1252    * Bean property getter:  <property>writeOnly</property>.
1253    *
1254    * <p>
1255    * This property was added in Draft 07.
1256    *
1257    * @return The value of the <property>writeOnly</property> property on this bean, or <jk>null</jk> if it is not set.
1258    */
1259   public Boolean getWriteOnly() { return writeOnly; }
1260
1261   /**
1262    * Resolve schema if reference.
1263    *
1264    * <p>
1265    * If this schema is a reference to another schema (has its <property>$ref</property> property set), this
1266    * method will retrieve the referenced schema from the schema map registered with this schema.
1267    *
1268    * <p>
1269    * If this schema is not a reference, or no schema map is registered with this schema, this method is a no-op and
1270    * simply returns this object.
1271    *
1272    * @return The referenced schema, or <jk>null</jk>.
1273    */
1274   public JsonSchema resolve() {
1275      if (ref == null || master.schemaMap == null)
1276         return this;
1277      return master.schemaMap.get(ref);
1278   }
1279
1280   /**
1281    * Bean property setter:  <property>additionalItems</property>.
1282    *
1283    * @param additionalItems
1284    *    The new value for the <property>additionalItems</property> property on this bean.
1285    *    This object must be of type {@link Boolean} or {@link JsonSchemaArray}.
1286    * @return This object.
1287    * @throws BeanRuntimeException If invalid object type passed in.
1288    */
1289   public JsonSchema setAdditionalItems(Object additionalItems) {
1290      this.additionalItemsBoolean = null;
1291      this.additionalItemsSchemaArray = null;
1292      if (nn(additionalItems)) {
1293         if (additionalItems instanceof Boolean additionalItems2)
1294            this.additionalItemsBoolean = additionalItems2;
1295         else if (additionalItems instanceof JsonSchemaArray additionalItems2) {
1296            this.additionalItemsSchemaArray = additionalItems2;
1297            setMasterOn(this.additionalItemsSchemaArray);
1298         } else {
1299            throw bex(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in.  Must be one of the following:  Boolean, JsonSchemaArray", cn(additionalItems));
1300         }
1301      }
1302      return this;
1303   }
1304
1305   /**
1306    * Bean property setter:  <property>additionalProperties</property>.
1307    *
1308    * @param additionalProperties
1309    *    The new value for the <property>additionalProperties</property> property on this bean.
1310    *    This object must be of type {@link Boolean} or {@link JsonSchema}.
1311    * @return This object.
1312    * @throws BeanRuntimeException If invalid object type passed in.
1313    */
1314   @Beanp(dictionary = { JsonSchema.class })
1315   public JsonSchema setAdditionalProperties(Object additionalProperties) {
1316      this.additionalPropertiesBoolean = null;
1317      this.additionalPropertiesSchema = null;
1318      if (nn(additionalProperties)) {
1319         if (additionalProperties instanceof Boolean additionalProperties2)
1320            this.additionalPropertiesBoolean = additionalProperties2;
1321         else if (additionalProperties instanceof JsonSchema additionalProperties2) {
1322            this.additionalPropertiesSchema = additionalProperties2;
1323            setMasterOn(this.additionalPropertiesSchema);
1324         } else
1325            throw bex(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in.  Must be one of the following:  Boolean, JsonSchema", cn(additionalProperties));
1326      }
1327      return this;
1328   }
1329
1330   /**
1331    * Bean property setter:  <property>allOf</property>.
1332    *
1333    * @param allOf The new value for the <property>allOf</property> property on this bean.
1334    * @return This object.
1335    */
1336   public JsonSchema setAllOf(List<JsonSchema> allOf) {
1337      this.allOf = allOf;
1338      setMasterOn(allOf);
1339      return this;
1340   }
1341
1342   /**
1343    * Bean property setter:  <property>anyOf</property>.
1344    *
1345    * @param anyOf The new value of the <property>anyOf</property> property on this bean.
1346    * @return This object.
1347    */
1348   public JsonSchema setAnyOf(List<JsonSchema> anyOf) {
1349      this.anyOf = anyOf;
1350      setMasterOn(anyOf);
1351      return this;
1352   }
1353
1354   /**
1355    * Bean property setter:  <property>const</property>.
1356    *
1357    * <p>
1358    * This property was added in Draft 06.
1359    *
1360    * @param const_ The new value for the <property>const</property> property on this bean.
1361    * @return This object.
1362    */
1363   public JsonSchema setConst(Object const_) {
1364      this.const_ = const_;
1365      return this;
1366   }
1367
1368   /**
1369    * Bean property setter:  <property>contentEncoding</property>.
1370    *
1371    * <p>
1372    * This property was added in Draft 07.
1373    *
1374    * @param contentEncoding The new value for the <property>contentEncoding</property> property on this bean.
1375    * @return This object.
1376    */
1377   public JsonSchema setContentEncoding(String contentEncoding) {
1378      this.contentEncoding = contentEncoding;
1379      return this;
1380   }
1381
1382   /**
1383    * Bean property setter:  <property>contentMediaType</property>.
1384    *
1385    * <p>
1386    * This property was added in Draft 07.
1387    *
1388    * @param contentMediaType The new value for the <property>contentMediaType</property> property on this bean.
1389    * @return This object.
1390    */
1391   public JsonSchema setContentMediaType(String contentMediaType) {
1392      this.contentMediaType = contentMediaType;
1393      return this;
1394   }
1395
1396   /**
1397    * Bean property setter:  <property>definitions</property>.
1398    *
1399    * @param definitions The new value for the <property>definitions</property> property on this bean.
1400    * @return This object.
1401    */
1402   public JsonSchema setDefinitions(Map<String,JsonSchema> definitions) {
1403      this.definitions = definitions;
1404      if (nn(definitions))
1405         setMasterOn(definitions.values());
1406      return this;
1407   }
1408
1409   /**
1410    * Bean property setter:  <property>$defs</property>.
1411    *
1412    * <p>
1413    * This is the Draft 2020-12 replacement for <property>definitions</property>.
1414    * Both properties are supported for backward compatibility.
1415    *
1416    * @param defs The new value for the <property>$defs</property> property on this bean.
1417    * @return This object.
1418    */
1419   @Beanp("$defs")
1420   public JsonSchema setDefs(Map<String,JsonSchema> defs) {
1421      this.defs = defs;
1422      if (nn(defs))
1423         setMasterOn(defs.values());
1424      return this;
1425   }
1426
1427   /**
1428    * Bean property setter:  <property>dependencies</property>.
1429    *
1430    * @param dependencies The new value for the <property>dependencies</property> property on this bean.
1431    * @return This object.
1432    */
1433   public JsonSchema setDependencies(Map<String,JsonSchema> dependencies) {
1434      this.dependencies = dependencies;
1435      if (nn(dependencies))
1436         setMasterOn(dependencies.values());
1437      return this;
1438   }
1439
1440   /**
1441    * Bean property setter:  <property>dependentRequired</property>.
1442    *
1443    * <p>
1444    * This property was added in Draft 2019-09 as a replacement for the array form of <property>dependencies</property>.
1445    *
1446    * @param dependentRequired The new value for the <property>dependentRequired</property> property on this bean.
1447    * @return This object.
1448    */
1449   public JsonSchema setDependentRequired(Map<String,List<String>> dependentRequired) {
1450      this.dependentRequired = dependentRequired;
1451      return this;
1452   }
1453
1454   /**
1455    * Bean property setter:  <property>dependentSchemas</property>.
1456    *
1457    * <p>
1458    * This property was added in Draft 2019-09 as a replacement for the schema form of <property>dependencies</property>.
1459    *
1460    * @param dependentSchemas The new value for the <property>dependentSchemas</property> property on this bean.
1461    * @return This object.
1462    */
1463   public JsonSchema setDependentSchemas(Map<String,JsonSchema> dependentSchemas) {
1464      this.dependentSchemas = dependentSchemas;
1465      if (nn(dependentSchemas))
1466         setMasterOn(dependentSchemas.values());
1467      return this;
1468   }
1469
1470   /**
1471    * Bean property setter:  <property>description</property>.
1472    *
1473    * @param description The new value for the <property>description</property> property on this bean.
1474    * @return This object.
1475    */
1476   public JsonSchema setDescription(String description) {
1477      this.description = description;
1478      return this;
1479   }
1480
1481   /**
1482    * Bean property setter:  <property>else</property>.
1483    *
1484    * <p>
1485    * This property was added in Draft 07 for conditional schema application.
1486    *
1487    * @param else_ The new value for the <property>else</property> property on this bean.
1488    * @return This object.
1489    */
1490   @Beanp("else")
1491   public JsonSchema setElse(JsonSchema else_) {
1492      this.else_ = else_;
1493      setMasterOn(else_);
1494      return this;
1495   }
1496
1497   /**
1498    * Bean property setter:  <property>enum</property>.
1499    *
1500    * @param enum_ The new value for the <property>enum</property> property on this bean.
1501    * @return This object.
1502    */
1503   public JsonSchema setEnum(List<Object> enum_) {
1504      this.enum_ = enum_;
1505      return this;
1506   }
1507
1508   /**
1509    * Bean property setter:  <property>examples</property>.
1510    *
1511    * <p>
1512    * This property was added in Draft 06.
1513    *
1514    * @param examples The new value for the <property>examples</property> property on this bean.
1515    * @return This object.
1516    */
1517   public JsonSchema setExamples(List<Object> examples) {
1518      this.examples = examples;
1519      return this;
1520   }
1521
1522   /**
1523    * Bean property setter:  <property>exclusiveMaximum</property>.
1524    *
1525    * <p>
1526    * In Draft 06+, this is a numeric value representing the exclusive upper bound.
1527    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1528    *
1529    * @param exclusiveMaximum The new value for the <property>exclusiveMaximum</property> property on this bean.
1530    * @return This object.
1531    */
1532   public JsonSchema setExclusiveMaximum(Number exclusiveMaximum) {
1533      this.exclusiveMaximum = exclusiveMaximum;
1534      return this;
1535   }
1536
1537   /**
1538    * Bean property setter:  <property>exclusiveMinimum</property>.
1539    *
1540    * <p>
1541    * In Draft 06+, this is a numeric value representing the exclusive lower bound.
1542    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1543    *
1544    * @param exclusiveMinimum The new value for the <property>exclusiveMinimum</property> property on this bean.
1545    * @return This object.
1546    */
1547   public JsonSchema setExclusiveMinimum(Number exclusiveMinimum) {
1548      this.exclusiveMinimum = exclusiveMinimum;
1549      return this;
1550   }
1551
1552   /**
1553    * Bean property setter:  <property>id</property>.
1554    *
1555    * <p>
1556    * <b>Deprecated:</b> Use {@link #setIdUri(Object)} instead.
1557    * This property is retained for Draft 04 backward compatibility.
1558    *
1559    * <p>
1560    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1561    * Strings must be valid URIs.
1562    *
1563    * <p>
1564    * URIs defined by {@link UriResolver} can be used for values.
1565    *
1566    * @param id The new value for the <property>id</property> property on this bean.
1567    * @return This object.
1568    * @deprecated Use {@link #setIdUri(Object)} instead.
1569    */
1570   @Deprecated
1571   public JsonSchema setId(Object id) {
1572      this.id = toUri(id);
1573      return this;
1574   }
1575
1576   /**
1577    * Bean property setter:  <property>$id</property>.
1578    *
1579    * <p>
1580    * This is the Draft 2020-12 property for schema identification.
1581    *
1582    * <p>
1583    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1584    * Strings must be valid URIs.
1585    *
1586    * <p>
1587    * URIs defined by {@link UriResolver} can be used for values.
1588    *
1589    * @param idUri The new value for the <property>$id</property> property on this bean.
1590    * @return This object.
1591    */
1592   @Beanp("$id")
1593   public JsonSchema setIdUri(Object idUri) {
1594      this.idUri = toUri(idUri);
1595      return this;
1596   }
1597
1598   /**
1599    * Bean property setter:  <property>if</property>.
1600    *
1601    * <p>
1602    * This property was added in Draft 07 for conditional schema application.
1603    *
1604    * @param if_ The new value for the <property>if</property> property on this bean.
1605    * @return This object.
1606    */
1607   @Beanp("if")
1608   public JsonSchema setIf(JsonSchema if_) {
1609      this.if_ = if_;
1610      setMasterOn(if_);
1611      return this;
1612   }
1613
1614   /**
1615    * Bean property setter:  <property>items</property>.
1616    *
1617    * @param
1618    *    items The new value for the <property>items</property> property on this bean.
1619    *    This object must be of type {@link JsonSchema} or {@link JsonSchemaArray}.
1620    * @return This object.
1621    * @throws BeanRuntimeException If invalid object type passed in.
1622    */
1623   public JsonSchema setItems(Object items) {
1624      this.itemsSchema = null;
1625      this.itemsSchemaArray = null;
1626      if (nn(items)) {
1627         if (items instanceof JsonSchema items2) {
1628            this.itemsSchema = items2;
1629            setMasterOn(this.itemsSchema);
1630         } else if (items instanceof JsonSchemaArray items2) {
1631            this.itemsSchemaArray = items2;
1632            setMasterOn(this.itemsSchemaArray);
1633         } else {
1634            throw bex(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in.  Must be one of the following:  JsonSchema, JsonSchemaArray", cn(items));
1635         }
1636      }
1637      return this;
1638   }
1639
1640   /**
1641    * Bean property setter:  <property>maximum</property>.
1642    *
1643    * @param maximum The new value for the <property>maximum</property> property on this bean.
1644    * @return This object.
1645    */
1646   public JsonSchema setMaximum(Number maximum) {
1647      this.maximum = maximum;
1648      return this;
1649   }
1650
1651   /**
1652    * Bean property setter:  <property>maxItems</property>.
1653    *
1654    * @param maxItems The new value for the <property>maxItems</property> property on this bean.
1655    * @return This object.
1656    */
1657   public JsonSchema setMaxItems(Integer maxItems) {
1658      this.maxItems = maxItems;
1659      return this;
1660   }
1661
1662   /**
1663    * Bean property setter:  <property>maxLength</property>.
1664    *
1665    * @param maxLength The new value for the <property>maxLength</property> property on this bean.
1666    * @return This object.
1667    */
1668   public JsonSchema setMaxLength(Integer maxLength) {
1669      this.maxLength = maxLength;
1670      return this;
1671   }
1672
1673   /**
1674    * Bean property setter:  <property>maxProperties</property>.
1675    *
1676    * @param maxProperties The new value for the <property>maxProperties</property> property on this bean.
1677    * @return This object.
1678    */
1679   public JsonSchema setMaxProperties(Integer maxProperties) {
1680      this.maxProperties = maxProperties;
1681      return this;
1682   }
1683
1684   /**
1685    * Bean property setter:  <property>minimum</property>.
1686    *
1687    * @param minimum The new value for the <property>minimum</property> property on this bean.
1688    * @return This object.
1689    */
1690   public JsonSchema setMinimum(Number minimum) {
1691      this.minimum = minimum;
1692      return this;
1693   }
1694
1695   /**
1696    * Bean property setter:  <property>minItems</property>.
1697    *
1698    * @param minItems The new value for the <property>minItems</property> property on this bean.
1699    * @return This object.
1700    */
1701   public JsonSchema setMinItems(Integer minItems) {
1702      this.minItems = minItems;
1703      return this;
1704   }
1705
1706   /**
1707    * Bean property setter:  <property>minLength</property>.
1708    *
1709    * @param minLength The new value for the <property>minLength</property> property on this bean.
1710    * @return This object.
1711    */
1712   public JsonSchema setMinLength(Integer minLength) {
1713      this.minLength = minLength;
1714      return this;
1715   }
1716
1717   /**
1718    * Bean property setter:  <property>minProperties</property>.
1719    *
1720    * @param minProperties The new value for the <property>minProperties</property> property on this bean.
1721    * @return This object.
1722    */
1723   public JsonSchema setMinProperties(Integer minProperties) {
1724      this.minProperties = minProperties;
1725      return this;
1726   }
1727
1728   /**
1729    * Bean property setter:  <property>multipleOf</property>.
1730    *
1731    * @param multipleOf The new value for the <property>multipleOf</property> property on this bean.
1732    * @return This object.
1733    */
1734   public JsonSchema setMultipleOf(Number multipleOf) {
1735      this.multipleOf = multipleOf;
1736      return this;
1737   }
1738
1739   /**
1740    * Bean property setter:  <property>name</property>.
1741    *
1742    * <p>
1743    * This is an internal property used for tracking property names and is not part of the JSON Schema specification.
1744    *
1745    * @param name The new value for the <property>name</property> property on this bean.
1746    * @return This object.
1747    */
1748   @BeanIgnore
1749   public JsonSchema setName(String name) {
1750      this.name = name;
1751      return this;
1752   }
1753
1754   /**
1755    * Bean property setter:  <property>not</property>.
1756    *
1757    * @param not The new value for the <property>not</property> property on this bean.
1758    * @return This object.
1759    */
1760   public JsonSchema setNot(JsonSchema not) {
1761      this.not = not;
1762      setMasterOn(not);
1763      return this;
1764   }
1765
1766   /**
1767    * Bean property setter:  <property>oneOf</property>.
1768    *
1769    * @param oneOf The new value for the <property>oneOf</property> property on this bean.
1770    * @return This object.
1771    */
1772   public JsonSchema setOneOf(List<JsonSchema> oneOf) {
1773      this.oneOf = oneOf;
1774      setMasterOn(oneOf);
1775      return this;
1776   }
1777
1778   /**
1779    * Bean property setter:  <property>pattern</property>.
1780    *
1781    * @param pattern The new value for the <property>pattern</property> property on this bean.
1782    * @return This object.
1783    */
1784   public JsonSchema setPattern(String pattern) {
1785      this.pattern = pattern;
1786      return this;
1787   }
1788
1789   /**
1790    * Bean property setter:  <property>patternProperties</property>.
1791    *
1792    * @param patternProperties The new value for the <property>patternProperties</property> property on this bean.
1793    * @return This object.
1794    */
1795   public JsonSchema setPatternProperties(Map<String,JsonSchema> patternProperties) {
1796      this.patternProperties = patternProperties;
1797      if (nn(patternProperties)) {
1798         patternProperties.entrySet().forEach(x -> {
1799            var s = x.getValue();
1800            setMasterOn(s);
1801            s.setName(x.getKey());
1802         });
1803      }
1804      return this;
1805   }
1806
1807   /**
1808    * Bean property setter:  <property>prefixItems</property>.
1809    *
1810    * <p>
1811    * This property was added in Draft 2020-12 for tuple validation.
1812    *
1813    * @param prefixItems The new value for the <property>prefixItems</property> property on this bean.
1814    * @return This object.
1815    */
1816   public JsonSchema setPrefixItems(JsonSchemaArray prefixItems) {
1817      this.prefixItems = prefixItems;
1818      setMasterOn(prefixItems);
1819      return this;
1820   }
1821
1822   /**
1823    * Bean property setter:  <property>properties</property>.
1824    *
1825    * @param properties The new value for the <property>properties</property> property on this bean.
1826    * @return This object.
1827    */
1828   public JsonSchema setProperties(Map<String,JsonSchema> properties) {
1829      this.properties = properties;
1830      if (nn(properties)) {
1831         properties.entrySet().forEach(x -> {
1832            var value = x.getValue();
1833            setMasterOn(value);
1834            value.setName(x.getKey());
1835         });
1836      }
1837      return this;
1838   }
1839
1840   /**
1841    * Bean property setter:  <property>readOnly</property>.
1842    *
1843    * <p>
1844    * This property was added in Draft 07.
1845    *
1846    * @param readOnly The new value for the <property>readOnly</property> property on this bean.
1847    * @return This object.
1848    */
1849   public JsonSchema setReadOnly(Boolean readOnly) {
1850      this.readOnly = readOnly;
1851      return this;
1852   }
1853
1854   /**
1855    * Bean property setter:  <property>$ref</property>.
1856    *
1857    * <p>
1858    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1859    * Strings must be valid URIs.
1860    *
1861    * <p>
1862    * URIs defined by {@link UriResolver} can be used for values.
1863    *
1864    * @param ref The new value for the <property>$ref</property> property on this bean.
1865    * @return This object.
1866    */
1867   @Beanp("$ref")
1868   public JsonSchema setRef(Object ref) {
1869      this.ref = toUri(ref);
1870      return this;
1871   }
1872
1873   /**
1874    * Bean property setter:  <property>required</property>.
1875    *
1876    * @param required The new value for the <property>required</property> property on this bean.
1877    * @return This object.
1878    */
1879   public JsonSchema setRequired(List<String> required) {
1880      this.required = required;
1881      return this;
1882   }
1883
1884   /**
1885    * Associates a schema map with this schema for resolving other schemas identified through <property>$ref</property>
1886    * properties.
1887    *
1888    * @param schemaMap The schema map to associate with this schema.  Can be <jk>null</jk>.
1889    * @return This object.
1890    */
1891   @BeanIgnore
1892   public JsonSchema setSchemaMap(JsonSchemaMap schemaMap) {
1893      this.schemaMap = schemaMap;
1894      return this;
1895   }
1896
1897   /**
1898    * Bean property setter:  <property>$schema</property>.
1899    *
1900    * <p>
1901    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1902    * Strings must be valid URIs.
1903    *
1904    * <p>
1905    * URIs defined by {@link UriResolver} can be used for values.
1906    *
1907    * @param schemaVersion The new value for the <property>schemaVersion</property> property on this bean.
1908    * @return This object.
1909    */
1910   @Beanp("$schema")
1911   public JsonSchema setSchemaVersionUri(Object schemaVersion) {
1912      this.schemaVersion = toUri(schemaVersion);
1913      return this;
1914   }
1915
1916   /**
1917    * Bean property setter:  <property>then</property>.
1918    *
1919    * <p>
1920    * This property was added in Draft 07 for conditional schema application.
1921    *
1922    * @param then_ The new value for the <property>then</property> property on this bean.
1923    * @return This object.
1924    */
1925   @Beanp("then")
1926   public JsonSchema setThen(JsonSchema then_) {
1927      this.then_ = then_;
1928      setMasterOn(then_);
1929      return this;
1930   }
1931
1932   /**
1933    * Bean property setter:  <property>title</property>.
1934    *
1935    * @param title The new value for the <property>title</property> property on this bean.
1936    * @return This object.
1937    */
1938   public JsonSchema setTitle(String title) {
1939      this.title = title;
1940      return this;
1941   }
1942
1943   /**
1944    * Bean property setter:  <property>type</property>.
1945    *
1946    * @param type
1947    *    The new value for the <property>type</property> property on this bean.
1948    *    This object must be of type {@link JsonType} or {@link JsonTypeArray}.
1949    * @return This object.
1950    * @throws BeanRuntimeException If invalid object type passed in.
1951    */
1952   public JsonSchema setType(Object type) {
1953      this.typeJsonType = null;
1954      this.typeJsonTypeArray = null;
1955      if (nn(type)) {
1956         if (type instanceof JsonType type2)
1957            this.typeJsonType = type2;
1958         else if (type instanceof JsonTypeArray type2)
1959            this.typeJsonTypeArray = type2;
1960         else
1961            throw bex(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in.  Must be one of the following:  SimpleType, SimpleTypeArray", cn(type));
1962      }
1963      return this;
1964   }
1965
1966   /**
1967    * Bean property setter:  <property>unevaluatedItems</property>.
1968    *
1969    * <p>
1970    * This property was added in Draft 2019-09.
1971    *
1972    * @param unevaluatedItems The new value for the <property>unevaluatedItems</property> property on this bean.
1973    * @return This object.
1974    */
1975   public JsonSchema setUnevaluatedItems(JsonSchema unevaluatedItems) {
1976      this.unevaluatedItems = unevaluatedItems;
1977      setMasterOn(unevaluatedItems);
1978      return this;
1979   }
1980
1981   /**
1982    * Bean property setter:  <property>unevaluatedProperties</property>.
1983    *
1984    * <p>
1985    * This property was added in Draft 2019-09.
1986    *
1987    * @param unevaluatedProperties The new value for the <property>unevaluatedProperties</property> property on this bean.
1988    * @return This object.
1989    */
1990   public JsonSchema setUnevaluatedProperties(JsonSchema unevaluatedProperties) {
1991      this.unevaluatedProperties = unevaluatedProperties;
1992      setMasterOn(unevaluatedProperties);
1993      return this;
1994   }
1995
1996   /**
1997    * Bean property setter:  <property>uniqueItems</property>.
1998    *
1999    * @param uniqueItems The new value for the <property>uniqueItems</property> property on this bean.
2000    * @return This object.
2001    */
2002   public JsonSchema setUniqueItems(Boolean uniqueItems) {
2003      this.uniqueItems = uniqueItems;
2004      return this;
2005   }
2006
2007   /**
2008    * Bean property setter:  <property>writeOnly</property>.
2009    *
2010    * <p>
2011    * This property was added in Draft 07.
2012    *
2013    * @param writeOnly The new value for the <property>writeOnly</property> property on this bean.
2014    * @return This object.
2015    */
2016   public JsonSchema setWriteOnly(Boolean writeOnly) {
2017      this.writeOnly = writeOnly;
2018      return this;
2019   }
2020
2021   @Override /* Overridden from Object */
2022   public String toString() {
2023      return JsonSerializer.DEFAULT_SORTED.toString(this);
2024   }
2025
2026   private void setMasterOn(Collection<JsonSchema> ss) {
2027      if (nn(ss))
2028         ss.forEach(this::setMasterOn);
2029   }
2030
2031   private void setMasterOn(JsonSchema s) {
2032      if (nn(s))
2033         s.setMaster(this);
2034   }
2035
2036   private void setMasterOn(JsonSchema[] ss) {
2037      if (nn(ss))
2038         for (var s : ss)
2039            setMasterOn(s);
2040   }
2041
2042   private void setMasterOn(JsonSchemaArray ss) {
2043      if (nn(ss))
2044         ss.forEach(this::setMasterOn);
2045   }
2046
2047   /**
2048    * Sets the master schema for this schema and all child schema objects.
2049    *
2050    * <p>
2051    * All child elements in a schema should point to a single "master" schema in order to locate registered JsonSchemaMap
2052    * objects for resolving external schemas.
2053    *
2054    * @param master The master schema to associate on this and all children.  Can be <jk>null</jk>.
2055    */
2056   protected void setMaster(JsonSchema master) {
2057      this.master = master;
2058      if (nn(definitions))
2059         definitions.values().forEach(x -> x.setMaster(master));
2060      if (nn(defs))
2061         defs.values().forEach(x -> x.setMaster(master));
2062      if (nn(properties))
2063         properties.values().forEach(x -> x.setMaster(master));
2064      if (nn(patternProperties))
2065         patternProperties.values().forEach(x -> x.setMaster(master));
2066      if (nn(dependencies))
2067         dependencies.values().forEach(x -> x.setMaster(master));
2068      if (nn(dependentSchemas))
2069         dependentSchemas.values().forEach(x -> x.setMaster(master));
2070      if (nn(itemsSchema))
2071         itemsSchema.setMaster(master);
2072      if (nn(itemsSchemaArray))
2073         itemsSchemaArray.forEach(x -> x.setMaster(master));
2074      if (nn(prefixItems))
2075         prefixItems.forEach(x -> x.setMaster(master));
2076      if (nn(additionalItemsSchemaArray))
2077         additionalItemsSchemaArray.forEach(x -> x.setMaster(master));
2078      if (nn(unevaluatedItems))
2079         unevaluatedItems.setMaster(master);
2080      if (nn(additionalPropertiesSchema))
2081         additionalPropertiesSchema.setMaster(master);
2082      if (nn(unevaluatedProperties))
2083         unevaluatedProperties.setMaster(master);
2084      if (nn(allOf))
2085         allOf.forEach(x -> x.setMaster(master));
2086      if (nn(anyOf))
2087         anyOf.forEach(x -> x.setMaster(master));
2088      if (nn(oneOf))
2089         oneOf.forEach(x -> x.setMaster(master));
2090      if (nn(not))
2091         not.setMaster(master);
2092      if (nn(if_))
2093         if_.setMaster(master);
2094      if (nn(then_))
2095         then_.setMaster(master);
2096      if (nn(else_))
2097         else_.setMaster(master);
2098   }
2099}