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.Shorts.*;
021import static org.apache.juneau.commons.utils.StringUtils.*;
022
023import java.lang.reflect.*;
024import java.net.*;
025import java.util.*;
026
027import org.apache.juneau.commons.bean.*;
028import org.apache.juneau.commons.reflect.*;
029import org.apache.juneau.marshall.*;
030import org.apache.juneau.marshall.marshaller.*;
031import org.apache.juneau.marshall.parser.*;
032import org.apache.juneau.marshall.serializer.*;
033import org.apache.juneau.marshall.swap.spi.*;
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</jsf>.write(<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 *    <li><b>Auto Generation:</b> Use {@link JsonSchemaBeanGenerator} to generate schemas from Java types
105 * </ul>
106 *
107 * <h5 class='section'>Common Use Cases:</h5>
108 *
109 * <p><b>1. Simple Type Constraints:</b>
110 * <p class='bjava'>
111 *    <jc>// String with length constraints</jc>
112 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
113 *       .setType(JsonType.<jsf>STRING</jsf>)
114 *       .setMinLength(5)
115 *       .setMaxLength(100)
116 *       .setPattern(<js>"^[A-Za-z]+$"</js>);
117 * </p>
118 *
119 * <p><b>2. Numeric Ranges:</b>
120 * <p class='bjava'>
121 *    <jc>// Number between 0 and 100 (exclusive)</jc>
122 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
123 *       .setType(JsonType.<jsf>NUMBER</jsf>)
124 *       .setExclusiveMinimum(0)
125 *       .setExclusiveMaximum(100)
126 *       .setMultipleOf(0.5);
127 * </p>
128 *
129 * <p><b>3. Enumerations:</b>
130 * <p class='bjava'>
131 *    <jc>// Status field with allowed values</jc>
132 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
133 *       .setType(JsonType.<jsf>STRING</jsf>)
134 *       .addEnum(<js>"pending"</js>, <js>"active"</js>, <js>"completed"</js>);
135 * </p>
136 *
137 * <p><b>4. Arrays:</b>
138 * <p class='bjava'>
139 *    <jc>// Array of strings with constraints</jc>
140 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
141 *       .setType(JsonType.<jsf>ARRAY</jsf>)
142 *       .setItems(<jk>new</jk> JsonSchema().setType(JsonType.<jsf>STRING</jsf>))
143 *       .setMinItems(1)
144 *       .setMaxItems(10)
145 *       .setUniqueItems(<jk>true</jk>);
146 * </p>
147 *
148 * <p><b>5. Conditional Schemas (Draft 07+):</b>
149 * <p class='bjava'>
150 *    <jc>// Different validation based on country</jc>
151 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
152 *       .setType(JsonType.<jsf>OBJECT</jsf>)
153 *       .addProperties(
154 *          <jk>new</jk> JsonSchemaProperty(<js>"country"</js>, JsonType.<jsf>STRING</jsf>),
155 *          <jk>new</jk> JsonSchemaProperty(<js>"postalCode"</js>, JsonType.<jsf>STRING</jsf>)
156 *       )
157 *       .setIf(<jk>new</jk> JsonSchema()
158 *          .addProperties(<jk>new</jk> JsonSchemaProperty(<js>"country"</js>).setConst(<js>"USA"</js>))
159 *       )
160 *       .setThen(<jk>new</jk> JsonSchema()
161 *          .addProperties(<jk>new</jk> JsonSchemaProperty(<js>"postalCode"</js>).setPattern(<js>"^[0-9]{5}$"</js>))
162 *       );
163 * </p>
164 *
165 * <p><b>6. Reusable Definitions:</b>
166 * <p class='bjava'>
167 *    <jc>// Schema with reusable components using $defs</jc>
168 *    JsonSchema <jv>schema</jv> = <jk>new</jk> JsonSchema()
169 *       .setType(JsonType.<jsf>OBJECT</jsf>)
170 *       .addDef(<js>"address"</js>, <jk>new</jk> JsonSchema()
171 *          .setType(JsonType.<jsf>OBJECT</jsf>)
172 *          .addProperties(
173 *             <jk>new</jk> JsonSchemaProperty(<js>"street"</js>, JsonType.<jsf>STRING</jsf>),
174 *             <jk>new</jk> JsonSchemaProperty(<js>"city"</js>, JsonType.<jsf>STRING</jsf>)
175 *          )
176 *       )
177 *       .addProperties(
178 *          <jk>new</jk> JsonSchemaProperty(<js>"billingAddress"</js>)
179 *             .setRef(<js>"#/$defs/address"</js>),
180 *          <jk>new</jk> JsonSchemaProperty(<js>"shippingAddress"</js>)
181 *             .setRef(<js>"#/$defs/address"</js>)
182 *       );
183 * </p>
184 *
185 * <h5 class='section'>Migration from Draft 04:</h5>
186 * <ul class='spaced-list'>
187 *    <li>Use {@link #setIdUri(Object)} instead of {@link #setId(Object)} (deprecated)
188 *    <li>Use {@link #setDefs(Map)} instead of {@link #setDefinitions(Map)} (deprecated but still works)
189 *    <li>Use {@link #setExclusiveMaximum(Number)} with a numeric value instead of a boolean flag
190 *    <li>Use {@link #setExclusiveMinimum(Number)} with a numeric value instead of a boolean flag
191 * </ul>
192 *
193 * <h5 class='section'>See Also:</h5><ul>
194 *    <li class='link'><a href="https://json-schema.org/draft/2020-12/json-schema-core.html">JSON Schema 2020-12 Core</a>
195 *    <li class='link'><a href="https://json-schema.org/draft/2020-12/json-schema-validation.html">JSON Schema 2020-12 Validation</a>
196 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema">juneau-bean-jsonschema</a>
197 * </ul>
198 *
199 * @param <SELF> The self type for fluent setters.
200 */
201@Marshalled(typeName = "schema")
202@SuppressWarnings({
203   "java:S116", // Field names follow OpenAPI/JSON Schema spec
204   "java:S119", // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
205   "java:S1133", // Kept for Draft 04 backward compatibility, will be removed in future version
206   "java:S1452", // The static factories of(Type)/of(Class) and the keyword accessors return JsonSchema<?> because SELF is unknown to callers.
207   "java:S6539" // Monster class; JsonSchema intentionally models the entire JSON Schema Draft 2020-12 keyword set as one cohesive bean
208})
209public class JsonSchema<SELF extends JsonSchema<SELF>> {
210
211   /**
212    * Used during parsing to convert the <property>additionalItems</property> property to the correct class type.
213    *
214    * <ul class='spaced-list'>
215    *    <li>
216    *       If parsing a JSON-array, converts to a {@link JsonSchemaArray}.
217    *    <li>
218    *       If parsing a JSON-boolean, converts to a {@link Boolean}.
219    * </ul>
220    *
221    * <p>
222    * Serialization method is a no-op.
223    */
224   public static class BooleanOrSchemaArraySwap extends ObjectSwap<Object,Object> {
225
226      @Override /* Overridden from ObjectSwap */
227      public Object swap(MarshallingSession session, Object o) throws SerializeException {
228         return o;
229      }
230
231      @Override /* Overridden from ObjectSwap */
232      public Object unswap(MarshallingSession session, Object o, ClassMeta<?> hint) throws ParseException {
233         var cm = session.getClassMeta(o instanceof Collection ? JsonSchemaArray.class : Boolean.class);
234         return session.convertToType(o, cm);
235      }
236   }
237
238   /**
239    * Used during parsing to convert the <property>additionalProperties</property> property to the correct class type.
240    *
241    * <ul class='spaced-list'>
242    *    <li>
243    *       If parsing a JSON-object, converts to a {@link JsonSchema}.
244    *    <li>
245    *       If parsing a JSON-boolean, converts to a {@link Boolean}.
246    * </ul>
247    *
248    * <p>
249    * Serialization method is a no-op.
250    */
251   public static class BooleanOrSchemaSwap extends ObjectSwap<Object,Object> {
252
253      @Override /* Overridden from ObjectSwap */
254      public Object swap(MarshallingSession session, Object o) throws SerializeException {
255         return o;
256      }
257
258      @Override /* Overridden from ObjectSwap */
259      public Object unswap(MarshallingSession session, Object o, ClassMeta<?> hint) throws ParseException {
260         var cm = session.getClassMeta(o instanceof Boolean ? Boolean.class : JsonSchema.class);
261         return session.convertToType(o, cm);
262      }
263   }
264
265   /**
266    * Used during parsing to convert the <property>items</property> property to the correct class type.
267    *
268    * <ul class='spaced-list'>
269    *    <li>
270    *       If parsing a JSON-array, converts to a {@link JsonSchemaArray}.
271    *    <li>
272    *       If parsing a JSON-object, converts to a {@link JsonSchema}.
273    * </ul>
274    *
275    * <p>
276    * Serialization method is a no-op.
277    */
278   public static class JsonSchemaOrSchemaArraySwap extends ObjectSwap<Object,Object> {
279
280      @Override /* Overridden from ObjectSwap */
281      public Object swap(MarshallingSession session, Object o) throws SerializeException {
282         return o;
283      }
284
285      @Override /* Overridden from ObjectSwap */
286      public Object unswap(MarshallingSession session, Object o, ClassMeta<?> hint) throws ParseException {
287         var cm = session.getClassMeta(o instanceof Collection ? JsonSchemaArray.class : JsonSchema.class);
288         return session.convertToType(o, cm);
289      }
290   }
291
292   /**
293    * Used during parsing to convert the <property>type</property> property to the correct class type.
294    *
295    * <ul class='spaced-list'>
296    *    <li>
297    *       If parsing a JSON-array, converts to a {@link JsonTypeArray}.
298    *    <li>
299    *       If parsing a JSON-object, converts to a {@link JsonType}.
300    * </ul>
301    *
302    * <p>
303    * Serialization method is a no-op.
304    */
305   public static class JsonTypeOrJsonTypeArraySwap extends ObjectSwap<Object,Object> {
306
307      @Override /* Overridden from ObjectSwap */
308      public Object swap(MarshallingSession session, Object o) throws SerializeException {
309         return o;
310      }
311
312      @Override /* Overridden from ObjectSwap */
313      public Object unswap(MarshallingSession session, Object o, ClassMeta<?> hint) throws ParseException {
314         var cm = session.getClassMeta(o instanceof Collection ? JsonTypeArray.class : JsonType.class);
315         return session.convertToType(o, cm);
316      }
317   }
318
319   // @formatter:off
320   private String name;                                   // Property name.  Not serialized.
321   private URI idUri;                                     // Draft 2020-12: $id
322   private URI id;                                        // Draft 04: id (deprecated but kept for compatibility)
323   private URI schemaVersion;
324   private String title;
325   private String summary;
326   private String description;
327   private JsonType typeJsonType;                         // JsonType representation of type
328   private JsonTypeArray typeJsonTypeArray;               // JsonTypeArray representation of type
329   private Map<String,JsonSchema<?>> definitions;            // Retained for backward compatibility
330   private Map<String,JsonSchema<?>> defs;                   // Draft 2020-12: $defs
331   private Map<String,JsonSchema<?>> properties;
332   private Map<String,JsonSchema<?>> patternProperties;
333   private Map<String,JsonSchema<?>> dependencies;           // Retained for backward compatibility
334   private Map<String,JsonSchema<?>> dependentSchemas;       // Draft 2019-09+
335   private Map<String,List<String>> dependentRequired;    // Draft 2019-09+
336   private JsonSchema<?> itemsSchema;                        // JsonSchema representation of items
337   private JsonSchemaArray itemsSchemaArray;              // JsonSchemaArray representation of items
338   private JsonSchemaArray prefixItems;                   // Draft 2020-12: replaces tuple validation
339   private Number multipleOf;
340   private Number maximum;
341   private Number exclusiveMaximum;                       // Draft 06+: changed from Boolean to Number
342   private Number minimum;
343   private Number exclusiveMinimum;                       // Draft 06+: changed from Boolean to Number
344   private Integer maxLength;
345   private Integer minLength;
346   private String pattern;
347   private Boolean additionalItemsBoolean;                // Boolean representation of additionalItems
348   private JsonSchemaArray additionalItemsSchemaArray;    // JsonSchemaArray representation of additionalItems
349   private JsonSchema<?> unevaluatedItems;                   // Draft 2019-09+
350   private Integer maxItems;
351   private Integer minItems;
352   private Boolean uniqueItems;
353   private Integer maxProperties;
354   private Integer minProperties;
355   private List<String> required;
356   private Boolean additionalPropertiesBoolean;           // Boolean representation of additionalProperties
357   private JsonSchema<?> additionalPropertiesSchema;         // JsonSchema representation of additionalProperties
358   private JsonSchema<?> unevaluatedProperties;              // Draft 2019-09+
359   private List<Object> enum_;                            // Changed to Object to support any type
360   private Object const_;                                 // Draft 06+
361   private List<Object> examples;                         // Draft 06+
362   private List<JsonSchema<?>> allOf;
363   private List<JsonSchema<?>> anyOf;
364   private List<JsonSchema<?>> oneOf;
365   private JsonSchema<?> not;
366   private JsonSchema<?> if_;                                // Draft 07+
367   private JsonSchema<?> then_;                              // Draft 07+
368   private JsonSchema<?> else_;                              // Draft 07+
369   private Boolean readOnly;                              // Draft 07+
370   private Boolean writeOnly;                             // Draft 07+
371   private String format;                                 // Annotation-level keyword
372   private String comment;                                // Draft 07+, serialized as $comment
373   private Boolean deprecated;                            // Draft 2019-09+
374   private String contentMediaType;                       // Draft 07+
375
376   private String contentEncoding;                        // Draft 07+
377
378   private URI ref;
379
380   private JsonSchemaMap schemaMap;
381
382   private JsonSchema<?> master = this;
383   // @formatter:on
384
385   /**
386    * Default constructor.
387    */
388   public JsonSchema() { /* Empty constructor. */ }
389
390   @SuppressWarnings({
391      "unchecked" // Standard CRTP self cast; SELF is always the concrete subclass.
392   })
393   private SELF self() {
394      return (SELF) this;
395   }
396
397   /**
398    * Generates a {@link JsonSchema} bean from the specified type using {@link JsonSchemaBeanGenerator#DEFAULT}.
399    *
400    * @param type The type to generate a schema for.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
401    * @return The generated schema bean, or <jk>null</jk> if a schema could not be generated for the type.
402    */
403   public static JsonSchema<?> of(Type type) {
404      return JsonSchemaBeanGenerator.DEFAULT.generate(type);
405   }
406
407   /**
408    * Generates a {@link JsonSchema} bean from the specified class using {@link JsonSchemaBeanGenerator#DEFAULT}.
409    *
410    * @param type The class to generate a schema for.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
411    * @return The generated schema bean, or <jk>null</jk> if a schema could not be generated for the class.
412    */
413   public static JsonSchema<?> of(Class<?> type) {
414      return JsonSchemaBeanGenerator.DEFAULT.generate(type);
415   }
416
417   /**
418    * Bean property appender:  <property>additionalItems</property>.
419    *
420    * @param value
421    *    The list of items to append to the <property>additionalItems</property> property on this bean.
422    * @return This object.
423    */
424   public SELF addAdditionalItems(JsonSchema<?>...value) {
425      if (this.additionalItemsSchemaArray == null)
426         this.additionalItemsSchemaArray = new JsonSchemaArray();
427      this.additionalItemsSchemaArray.addAll(value);
428      setMasterOn(value);
429      return self();
430   }
431
432   /**
433    * Bean property appender:  <property>allOf</property>.
434    *
435    * @param value The list of items to append to the <property>allOf</property> property on this bean.
436    * @return This object.
437    */
438   public SELF addAllOf(JsonSchema<?>...value) {
439      setMasterOn(value);
440      this.allOf = addAll(this.allOf, value);
441      return self();
442   }
443
444   /**
445    * Bean property appender:  <property>allOf</property>.
446    *
447    * @param value The collection of items to append to the <property>allOf</property> property on this bean.
448    * @return This object.
449    */
450   public SELF addAllOf(Collection<JsonSchema<?>> value) {
451      setMasterOn(value);
452      this.allOf = addAll(this.allOf, value == null ? null : new ArrayList<>(value));
453      return self();
454   }
455
456   /**
457    * Bean property appender:  <property>anyOf</property>.
458    *
459    * @param value The list of items to append to the <property>anyOf</property> property on this bean.
460    * @return This object.
461    */
462   public SELF addAnyOf(JsonSchema<?>...value) {
463      if (this.anyOf == null)
464         this.anyOf = new LinkedList<>();
465      setMasterOn(value);
466      for (var s : value)
467         this.anyOf.add(s);
468      return self();
469   }
470
471   /**
472    * Bean property appender:  <property>anyOf</property>.
473    *
474    * @param value The collection of items to append to the <property>anyOf</property> property on this bean.
475    * @return This object.
476    */
477   public SELF addAnyOf(Collection<JsonSchema<?>> value) {
478      if (this.anyOf == null)
479         this.anyOf = new LinkedList<>();
480      setMasterOn(value);
481      this.anyOf.addAll(value);
482      return self();
483   }
484
485   /**
486    * Bean property appender:  <property>$defs</property>.
487    *
488    * @param name The key in the defs map entry.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
489    * @param value The value in the defs map entry.  Can be <jk>null</jk> (stored as <jk>null</jk>).
490    * @return This object.
491    */
492   public SELF addDef(String name, JsonSchema<?> value) {
493      if (this.defs == null)
494         this.defs = map();
495      this.defs.put(name, value);
496      setMasterOn(value);
497      return self();
498   }
499
500   /**
501    * Bean property appender:  <property>definitions</property>.
502    *
503    * @param name The key in the definitions map entry.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
504    * @param value The value in the definitions map entry.  Can be <jk>null</jk> (stored as <jk>null</jk>).
505    * @return This object.
506    */
507   public SELF addDefinition(String name, JsonSchema<?> value) {
508      if (this.definitions == null)
509         this.definitions = map();
510      this.definitions.put(name, value);
511      setMasterOn(value);
512      return self();
513   }
514
515   /**
516    * Bean property appender:  <property>dependencies</property>.
517    *
518    * @param name The key of the entry in the dependencies map.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
519    * @param value The value of the entry in the dependencies map.  Can be <jk>null</jk> (stored as <jk>null</jk>).
520    * @return This object.
521    */
522   public SELF addDependency(String name, JsonSchema<?> value) {
523      if (this.dependencies == null)
524         this.dependencies = map();
525      this.dependencies.put(name, value);
526      setMasterOn(value);
527      return self();
528   }
529
530   /**
531    * Bean property appender:  <property>dependentRequired</property>.
532    *
533    * @param name The key of the entry in the dependentRequired map.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
534    * @param value The value of the entry in the dependentRequired map.  Can be <jk>null</jk> (stored as <jk>null</jk>).
535    * @return This object.
536    */
537   public SELF addDependentRequired(String name, List<String> value) {
538      if (this.dependentRequired == null)
539         this.dependentRequired = map();
540      this.dependentRequired.put(name, value);
541      return self();
542   }
543
544   /**
545    * Bean property appender:  <property>dependentSchemas</property>.
546    *
547    * @param name The key of the entry in the dependentSchemas map.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
548    * @param value The value of the entry in the dependentSchemas map.  Can be <jk>null</jk> (stored as <jk>null</jk>).
549    * @return This object.
550    */
551   public SELF addDependentSchema(String name, JsonSchema<?> value) {
552      if (this.dependentSchemas == null)
553         this.dependentSchemas = map();
554      this.dependentSchemas.put(name, value);
555      setMasterOn(value);
556      return self();
557   }
558
559   /**
560    * Bean property appender:  <property>enum</property>.
561    *
562    * @param value The list of items to append to the <property>enum</property> property on this bean.
563    * @return This object.
564    */
565   public SELF addEnum(Object...value) {
566      if (this.enum_ == null)
567         this.enum_ = new LinkedList<>();
568      for (var e : value)
569         this.enum_.add(e);
570      return self();
571   }
572
573   /**
574    * Bean property appender:  <property>enum</property>.
575    *
576    * @param value The collection of items to append to the <property>enum</property> property on this bean.
577    * @return This object.
578    */
579   public SELF addEnum(Collection<Object> value) {
580      if (this.enum_ == null)
581         this.enum_ = new LinkedList<>();
582      this.enum_.addAll(value);
583      return self();
584   }
585
586   /**
587    * Bean property appender:  <property>examples</property>.
588    *
589    * @param value The list of items to append to the <property>examples</property> property on this bean.
590    * @return This object.
591    */
592   public SELF addExamples(Object...value) {
593      if (this.examples == null)
594         this.examples = new LinkedList<>();
595      for (var e : value)
596         this.examples.add(e);
597      return self();
598   }
599
600   /**
601    * Bean property appender:  <property>examples</property>.
602    *
603    * @param value The collection of items to append to the <property>examples</property> property on this bean.
604    * @return This object.
605    */
606   public SELF addExamples(Collection<Object> value) {
607      if (this.examples == null)
608         this.examples = new LinkedList<>();
609      this.examples.addAll(value);
610      return self();
611   }
612
613   /**
614    * Bean property appender:  <property>items</property>.
615    *
616    * @param value The list of items to append to the <property>items</property> property on this bean.
617    * @return This object.
618    */
619   public SELF addItems(JsonSchema<?>...value) {
620      if (this.itemsSchemaArray == null)
621         this.itemsSchemaArray = new JsonSchemaArray();
622      this.itemsSchemaArray.addAll(value);
623      setMasterOn(value);
624      return self();
625   }
626
627   /**
628    * Bean property appender:  <property>oneOf</property>.
629    *
630    * @param value The list of items to append to the <property>oneOf</property> property on this bean.
631    * @return This object.
632    */
633   public SELF addOneOf(JsonSchema<?>...value) {
634      if (this.oneOf == null)
635         this.oneOf = new LinkedList<>();
636      setMasterOn(value);
637      for (var s : value)
638         this.oneOf.add(s);
639      return self();
640   }
641
642   /**
643    * Bean property appender:  <property>oneOf</property>.
644    *
645    * @param value The collection of items to append to the <property>oneOf</property> property on this bean.
646    * @return This object.
647    */
648   public SELF addOneOf(Collection<JsonSchema<?>> value) {
649      if (this.oneOf == null)
650         this.oneOf = new LinkedList<>();
651      setMasterOn(value);
652      this.oneOf.addAll(value);
653      return self();
654   }
655
656   /**
657    * Bean property appender:  <property>patternProperties</property>.
658    *
659    * <p>
660    * Properties must have their <property>name</property> property set to the pattern string when using this method.
661    *
662    * @param value The list of items to append to the <property>patternProperties</property> property on this bean.
663    * @return This object.
664    * @throws BeanRuntimeException If property is found without a set <property>name</property> property.
665    */
666   public SELF addPatternProperties(JsonSchemaProperty<?>...value) {
667      if (this.patternProperties == null)
668         this.patternProperties = map();
669      for (var p : value) {
670         if (p.getName() == null)
671            throw brex(JsonSchema.class, "Invalid property passed to JsonSchema.addProperties().  Property name was null.");
672         setMasterOn(p);
673         this.patternProperties.put(p.getName(), p);
674      }
675      return self();
676   }
677
678   /**
679    * Bean property appender:  <property>patternProperties</property>.
680    *
681    * <p>
682    * Unlike {@link #addPatternProperties(JsonSchemaProperty...)}, this keyed form does not require the value's
683    * <property>name</property> property to be pre-set — it is set automatically from {@code name}.
684    *
685    * @param name The key in the patternProperties map entry.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
686    * @param value The value in the patternProperties map entry.  Can be <jk>null</jk> (stored as <jk>null</jk>).
687    * @return This object.
688    */
689   public SELF addPatternProperty(String name, JsonSchema<?> value) {
690      if (this.patternProperties == null)
691         this.patternProperties = map();
692      if (value != null) {
693         setMasterOn(value);
694         value.setName(name);
695      }
696      this.patternProperties.put(name, value);
697      return self();
698   }
699
700   /**
701    * Bean property appender:  <property>prefixItems</property>.
702    *
703    * @param value The list of items to append to the <property>prefixItems</property> property on this bean.
704    * @return This object.
705    */
706   public SELF addPrefixItems(JsonSchema<?>...value) {
707      if (this.prefixItems == null)
708         this.prefixItems = new JsonSchemaArray();
709      this.prefixItems.addAll(value);
710      setMasterOn(value);
711      return self();
712   }
713
714   /**
715    * Bean property appender:  <property>properties</property>.
716    *
717    * <p>
718    * Properties must have their <property>name</property> property set on them when using this method.
719    *
720    * @param value The list of items to append to the <property>properties</property> property on this bean.
721    * @return This object.
722    * @throws BeanRuntimeException If property is found without a set <property>name</property> property.
723    */
724   public SELF addProperties(JsonSchema<?>...value) {
725      if (this.properties == null)
726         this.properties = map();
727      for (var p : value) {
728         if (p.getName() == null)
729            throw brex(JsonSchema.class, "Invalid property passed to JsonSchema.addProperties().  Property name was null.");
730         setMasterOn(p);
731         this.properties.put(p.getName(), p);
732      }
733      return self();
734   }
735
736   /**
737    * Bean property appender:  <property>properties</property>.
738    *
739    * <p>
740    * Unlike {@link #addProperties(JsonSchema...)}, this keyed form does not require the value's
741    * <property>name</property> property to be pre-set — it is set automatically from {@code name}.
742    *
743    * @param name The key in the properties map entry.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
744    * @param value The value in the properties map entry.  Can be <jk>null</jk> (stored as <jk>null</jk>).
745    * @return This object.
746    */
747   public SELF addProperty(String name, JsonSchema<?> value) {
748      if (this.properties == null)
749         this.properties = map();
750      if (value != null) {
751         setMasterOn(value);
752         value.setName(name);
753      }
754      this.properties.put(name, value);
755      return self();
756   }
757
758   /**
759    * Bean property appender:  <property>required</property>.
760    *
761    * @param value The list of items to append to the <property>required</property> property on this bean.
762    * @return This object.
763    */
764   public SELF addRequired(JsonSchemaProperty<?>...value) {
765      if (this.required == null)
766         this.required = new LinkedList<>();
767      for (var p : value)
768         this.required.add(p.getName());
769      return self();
770   }
771
772   /**
773    * Bean property appender:  <property>required</property>.
774    *
775    * @param value The list of items to append to the <property>required</property> property on this bean.
776    * @return This object.
777    */
778   public SELF addRequired(List<String> value) {
779      if (this.required == null)
780         this.required = new LinkedList<>();
781      value.forEach(x -> this.required.add(x));
782      return self();
783   }
784
785   /**
786    * Bean property appender:  <property>required</property>.
787    *
788    * @param value The list of items to append to the <property>required</property> property on this bean.
789    * @return This object.
790    */
791   public SELF addRequired(String...value) {
792      if (this.required == null)
793         this.required = new LinkedList<>();
794      for (var r : value)
795         this.required.add(r);
796      return self();
797   }
798
799   /**
800    * Bean property appender:  <property>type</property>.
801    *
802    * @param value The list of items to append to the <property>type</property> property on this bean.
803    * @return This object.
804    */
805   public SELF addTypes(JsonType...value) {
806      if (this.typeJsonTypeArray == null)
807         this.typeJsonTypeArray = new JsonTypeArray();
808      this.typeJsonTypeArray.addAll(value);
809      return self();
810   }
811
812   /**
813    * Bean property getter:  <property>additionalItems</property>.
814    *
815    * @return
816    *    The value of the <property>additionalItems</property> property on this bean, or <jk>null</jk> if it is
817    *    not set.
818    *    Can be either a {@link Boolean} or {@link JsonSchemaArray} depending on what value was used to set it.
819    */
820   @Swap(BooleanOrSchemaArraySwap.class)
821   public Object getAdditionalItems() {
822      if (nn(additionalItemsBoolean))
823         return additionalItemsBoolean;
824      return additionalItemsSchemaArray;
825   }
826
827   /**
828    * Bean property getter:  <property>additionalItems</property>.
829    *
830    * <p>
831    * Convenience method for returning the <property>additionalItems</property> property when it is a {@link Boolean}
832    * value.
833    *
834    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}.
835    */
836   @BeanIgnore
837   public Boolean getAdditionalItemsAsBoolean() { return additionalItemsBoolean; }
838
839   /**
840    * Bean property getter:  <property>additionalItems</property>.
841    *
842    * <p>
843    * Convenience method for returning the <property>additionalItems</property> property when it is a
844    * {@link JsonSchemaArray} value.
845    *
846    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}.
847    */
848   @BeanIgnore
849   public List<JsonSchema<?>> getAdditionalItemsAsSchemaArray() { return u(additionalItemsSchemaArray); }
850
851   /**
852    * Bean property getter:  <property>additionalProperties</property>.
853    *
854    * @return
855    *    The value of the <property>additionalProperties</property> property on this bean, or <jk>null</jk> if it
856    *    is not set.
857    *    Can be either a {@link Boolean} or {@link JsonSchema} depending on what value was used to set it.
858    */
859   @Swap(BooleanOrSchemaSwap.class)
860   public Object getAdditionalProperties() {
861      if (nn(additionalPropertiesBoolean))
862         return additionalPropertiesBoolean;
863      return additionalPropertiesSchema;
864   }
865
866   /**
867    * Bean property getter:  <property>additionalProperties</property>.
868    *
869    * <p>
870    * Convenience method for returning the <property>additionalProperties</property> property when it is a
871    * {@link Boolean} value.
872    *
873    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}.
874    */
875   @BeanIgnore
876   public Boolean getAdditionalPropertiesAsBoolean() { return additionalPropertiesBoolean; }
877
878   /**
879    * Bean property getter:  <property>additionalProperties</property>.
880    *
881    * <p>
882    * Convenience method for returning the <property>additionalProperties</property> property when it is a
883    * {@link JsonSchema} value.
884    *
885    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}.
886    */
887   @BeanIgnore
888   public JsonSchema<?> getAdditionalPropertiesAsSchema() { return additionalPropertiesSchema; }
889
890   /**
891    * Bean property getter:  <property>allOf</property>.
892    *
893    * @return The value of the <property>allOf</property> property on this bean, or <jk>null</jk> if it is not set.
894    */
895   public List<JsonSchema<?>> getAllOf() { return u(allOf); }
896
897   /**
898    * Bean property getter:  <property>anyOf</property>.
899    *
900    * @return The value of the <property>anyOf</property> property on this bean, or <jk>null</jk> if it is not set.
901    */
902   public List<JsonSchema<?>> getAnyOf() { return u(anyOf); }
903
904   /**
905    * Bean property getter:  <property>const</property>.
906    *
907    * <p>
908    * This property was added in Draft 06.
909    *
910    * @return The value of the <property>const</property> property on this bean, or <jk>null</jk> if it is not set.
911    */
912   public Object getConst() { return const_; }
913
914   /**
915    * Bean property getter:  <property>contentEncoding</property>.
916    *
917    * <p>
918    * This property was added in Draft 07.
919    *
920    * @return The value of the <property>contentEncoding</property> property on this bean, or <jk>null</jk> if it is not set.
921    */
922   public String getContentEncoding() { return contentEncoding; }
923
924   /**
925    * Bean property getter:  <property>contentMediaType</property>.
926    *
927    * <p>
928    * This property was added in Draft 07.
929    *
930    * @return The value of the <property>contentMediaType</property> property on this bean, or <jk>null</jk> if it is not set.
931    */
932   public String getContentMediaType() { return contentMediaType; }
933
934   /**
935    * Bean property getter:  <property>deprecated</property>.
936    *
937    * <p>
938    * This property was added in Draft 2019-09.
939    *
940    * @return The value of the <property>deprecated</property> property on this bean, or <jk>null</jk> if it is not set.
941    */
942   public Boolean getDeprecated() { return deprecated; }
943
944   /**
945    * Bean property getter:  <property>$comment</property>.
946    *
947    * <p>
948    * This property was added in Draft 07.
949    *
950    * @return The value of the <property>$comment</property> property on this bean, or <jk>null</jk> if it is not set.
951    */
952   @BeanProp("$comment")
953   public String getComment() { return comment; }
954
955   /**
956    * Bean property getter:  <property>definitions</property>.
957    *
958    * <p>
959    * <b>Deprecated:</b> Use {@link #getDefs()} for Draft 2020-12 compliance.
960    * This property is retained for Draft 04 backward compatibility.
961    *
962    * @return
963    *    The value of the <property>definitions</property> property on this bean, or <jk>null</jk> if it is not set.
964    */
965   public Map<String,JsonSchema<?>> getDefinitions() {
966      return nn(definitions) ? definitions : defs; // Fall back to $defs for compatibility
967   }
968
969   /**
970    * Bean property getter:  <property>$defs</property>.
971    *
972    * <p>
973    * This is the Draft 2020-12 replacement for <property>definitions</property>.
974    * Both properties are supported for backward compatibility.
975    *
976    * @return The value of the <property>$defs</property> property on this bean, or <jk>null</jk> if it is not set.
977    */
978   @BeanProp("$defs")
979   public Map<String,JsonSchema<?>> getDefs() {
980      return defs; // Return only defs, not definitions (to avoid double serialization)
981   }
982
983   /**
984    * Bean property getter:  <property>dependencies</property>.
985    *
986    * @return
987    *    The value of the <property>dependencies</property> property on this bean, or <jk>null</jk> if it is not set.
988    */
989   public Map<String,JsonSchema<?>> getDependencies() { return u(dependencies); }
990
991   /**
992    * Bean property getter:  <property>format</property>.
993    *
994    * @return The value of the <property>format</property> property on this bean, or <jk>null</jk> if it is not set.
995    */
996   public String getFormat() { return format; }
997
998   /**
999    * Bean property getter:  <property>dependentRequired</property>.
1000    *
1001    * <p>
1002    * This property was added in Draft 2019-09 as a replacement for the array form of <property>dependencies</property>.
1003    *
1004    * @return The value of the <property>dependentRequired</property> property on this bean, or <jk>null</jk> if it is not set.
1005    */
1006   public Map<String,List<String>> getDependentRequired() { return u(dependentRequired); }
1007
1008   /**
1009    * Bean property getter:  <property>dependentSchemas</property>.
1010    *
1011    * <p>
1012    * This property was added in Draft 2019-09 as a replacement for the schema form of <property>dependencies</property>.
1013    *
1014    * @return The value of the <property>dependentSchemas</property> property on this bean, or <jk>null</jk> if it is not set.
1015    */
1016   public Map<String,JsonSchema<?>> getDependentSchemas() { return u(dependentSchemas); }
1017
1018   /**
1019    * Bean property getter:  <property>description</property>.
1020    *
1021    * @return The value of the <property>description</property> property, or <jk>null</jk> if it is not set.
1022    */
1023   public String getDescription() { return description; }
1024
1025   /**
1026    * Bean property getter:  <property>else</property>.
1027    *
1028    * <p>
1029    * This property was added in Draft 07 for conditional schema application.
1030    *
1031    * @return The value of the <property>else</property> property on this bean, or <jk>null</jk> if it is not set.
1032    */
1033   @BeanProp("else")
1034   public JsonSchema<?> getElse() { return else_; }
1035
1036   /**
1037    * Bean property getter:  <property>enum</property>.
1038    *
1039    * @return The value of the <property>enum</property> property on this bean, or <jk>null</jk> if it is not set.
1040    */
1041   public List<Object> getEnum() { return u(enum_); }
1042
1043   /**
1044    * Bean property getter:  <property>examples</property>.
1045    *
1046    * <p>
1047    * This property was added in Draft 06.
1048    *
1049    * @return The value of the <property>examples</property> property on this bean, or <jk>null</jk> if it is not set.
1050    */
1051   public List<Object> getExamples() { return u(examples); }
1052
1053   /**
1054    * Bean property getter:  <property>exclusiveMaximum</property>.
1055    *
1056    * <p>
1057    * In Draft 06+, this is a numeric value representing the exclusive upper bound.
1058    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1059    *
1060    * @return
1061    *    The value of the <property>exclusiveMaximum</property> property on this bean, or <jk>null</jk> if it is
1062    *    not set.
1063    */
1064   public Number getExclusiveMaximum() { return exclusiveMaximum; }
1065
1066   /**
1067    * Bean property getter:  <property>exclusiveMinimum</property>.
1068    *
1069    * <p>
1070    * In Draft 06+, this is a numeric value representing the exclusive lower bound.
1071    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1072    *
1073    * @return
1074    *    The value of the <property>exclusiveMinimum</property> property on this bean, or <jk>null</jk> if it is
1075    *    not set.
1076    */
1077   public Number getExclusiveMinimum() { return exclusiveMinimum; }
1078
1079   /**
1080    * Bean property getter:  <property>id</property>.
1081    *
1082    * <p>
1083    * <b>Deprecated:</b> Use {@link #getIdUri()} instead.
1084    * This property is retained for Draft 04 backward compatibility.
1085    *
1086    * @return The value of the <property>id</property> property on this bean, or <jk>null</jk> if it is not set.
1087    * @deprecated Use {@link #getIdUri()} instead.
1088    */
1089   @Deprecated(since = "10.0", forRemoval = true)
1090   public URI getId() {
1091      return nn(id) ? id : idUri; // Fall back to new '$id' for compatibility when reading
1092   }
1093
1094   /**
1095    * Bean property getter:  <property>$id</property>.
1096    *
1097    * <p>
1098    * This is the Draft 2020-12 property for schema identification.
1099    *
1100    * @return The value of the <property>$id</property> property on this bean, or <jk>null</jk> if it is not set.
1101    */
1102   @BeanProp("$id")
1103   public URI getIdUri() {
1104      return idUri; // Return only idUri, not id (to avoid double serialization)
1105   }
1106
1107   /**
1108    * Bean property getter:  <property>if</property>.
1109    *
1110    * <p>
1111    * This property was added in Draft 07 for conditional schema application.
1112    *
1113    * @return The value of the <property>if</property> property on this bean, or <jk>null</jk> if it is not set.
1114    */
1115   @BeanProp("if")
1116   public JsonSchema<?> getIf() { return if_; }
1117
1118   /**
1119    * Bean property getter:  <property>items</property>.
1120    *
1121    * @return
1122    *    The value of the <property>items</property> property on this bean, or <jk>null</jk> if it is not set.
1123    *    Can be either a {@link JsonSchema} or {@link JsonSchemaArray} depending on what value was used to set it.
1124    */
1125   @Swap(JsonSchemaOrSchemaArraySwap.class)
1126   public Object getItems() {
1127      if (nn(itemsSchema))
1128         return itemsSchema;
1129      return itemsSchemaArray;
1130   }
1131
1132   /**
1133    * Bean property getter:  <property>items</property>.
1134    *
1135    * <p>
1136    * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchema} value.
1137    *
1138    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}.
1139    */
1140   @BeanIgnore
1141   public JsonSchema<?> getItemsAsSchema() { return itemsSchema; }
1142
1143   /**
1144    * Bean property getter:  <property>items</property>.
1145    *
1146    * <p>
1147    * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchemaArray} value.
1148    *
1149    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}.
1150    */
1151   @BeanIgnore
1152   public JsonSchemaArray getItemsAsSchemaArray() { return itemsSchemaArray; }
1153
1154   /**
1155    * Bean property getter:  <property>maximum</property>.
1156    *
1157    * @return The value of the <property>maximum</property> property on this bean, or <jk>null</jk> if it is not set.
1158    */
1159   public Number getMaximum() { return maximum; }
1160
1161   /**
1162    * Bean property getter:  <property>maxItems</property>.
1163    *
1164    * @return The value of the <property>maxItems</property> property on this bean, or <jk>null</jk> if it is not set.
1165    */
1166   public Integer getMaxItems() { return maxItems; }
1167
1168   /**
1169    * Bean property getter:  <property>maxLength</property>.
1170    *
1171    * @return The value of the <property>maxLength</property> property on this bean, or <jk>null</jk> if it is not set.
1172    */
1173   public Integer getMaxLength() { return maxLength; }
1174
1175   /**
1176    * Bean property getter:  <property>maxProperties</property>.
1177    *
1178    * @return
1179    *    The value of the <property>maxProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1180    */
1181   public Integer getMaxProperties() { return maxProperties; }
1182
1183   /**
1184    * Bean property getter:  <property>minimum</property>.
1185    *
1186    * @return The value of the <property>minimum</property> property on this bean, or <jk>null</jk> if it is not set.
1187    */
1188   public Number getMinimum() { return minimum; }
1189
1190   /**
1191    * Bean property getter:  <property>minItems</property>.
1192    *
1193    * @return The value of the <property>minItems</property> property on this bean, or <jk>null</jk> if it is not set.
1194    */
1195   public Integer getMinItems() { return minItems; }
1196
1197   /**
1198    * Bean property getter:  <property>minLength</property>.
1199    *
1200    * @return The value of the <property>minLength</property> property on this bean, or <jk>null</jk> if it is not set.
1201    */
1202   public Integer getMinLength() { return minLength; }
1203
1204   /**
1205    * Bean property getter:  <property>minProperties</property>.
1206    *
1207    * @return
1208    *    The value of the <property>minProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1209    */
1210   public Integer getMinProperties() { return minProperties; }
1211
1212   /**
1213    * Bean property getter:  <property>multipleOf</property>.
1214    *
1215    * @return The value of the <property>multipleOf</property> property on this bean, or <jk>null</jk> if it is not set.
1216    */
1217   public Number getMultipleOf() { return multipleOf; }
1218
1219   /**
1220    * Bean property getter:  <property>name</property>.
1221    *
1222    * <p>
1223    * This is an internal property used for tracking property names and is not part of the JSON Schema specification.
1224    *
1225    * @return The value of the <property>name</property> property on this bean, or <jk>null</jk> if it is not set.
1226    */
1227   @BeanIgnore
1228   public String getName() { return name; }
1229
1230   /**
1231    * Bean property getter:  <property>not</property>.
1232    *
1233    * @return The value of the <property>not</property> property on this bean, or <jk>null</jk> if it is not set.
1234    */
1235   public JsonSchema<?> getNot() { return not; }
1236
1237   /**
1238    * Bean property getter:  <property>oneOf</property>.
1239    *
1240    * @return The value of the <property>oneOf</property> property on this bean, or <jk>null</jk> if it is not set.
1241    */
1242   public List<JsonSchema<?>> getOneOf() { return u(oneOf); }
1243
1244   /**
1245    * Bean property getter:  <property>pattern</property>.
1246    *
1247    * @return The value of the <property>pattern</property> property on this bean, or <jk>null</jk> if it is not set.
1248    */
1249   public String getPattern() { return pattern; }
1250
1251   /**
1252    * Bean property getter:  <property>patternProperties</property>.
1253    *
1254    * @return
1255    *    The value of the <property>patternProperties</property> property on this bean, or <jk>null</jk> if it is
1256    *    not set.
1257    */
1258   public Map<String,JsonSchema<?>> getPatternProperties() { return u(patternProperties); }
1259
1260   /**
1261    * Bean property getter:  <property>prefixItems</property>.
1262    *
1263    * <p>
1264    * This property was added in Draft 2020-12 for tuple validation.
1265    *
1266    * @return The value of the <property>prefixItems</property> property on this bean, or <jk>null</jk> if it is not set.
1267    */
1268   public JsonSchemaArray getPrefixItems() { return prefixItems; }
1269
1270   /**
1271    * Bean property getter:  <property>properties</property>.
1272    *
1273    * @return The value of the <property>properties</property> property on this bean, or <jk>null</jk> if it is not set.
1274    */
1275   public Map<String,JsonSchema<?>> getProperties() { return u(properties); }
1276
1277   /**
1278    * Returns the property with the specified name.
1279    *
1280    * <p>
1281    * This is equivalent to calling <property>getProperty(name, <jk>false</jk>)</property>.
1282    *
1283    * @param name The property name.  Can be <jk>null</jk> (no property has a <jk>null</jk> name, so <jk>null</jk> is returned).
1284    * @return The property with the specified name, or <jk>null</jk> if no property is specified.
1285    */
1286   public JsonSchema<?> getProperty(String name) {
1287      return getProperty(name, false);
1288   }
1289
1290   /**
1291    * Returns the property with the specified name.
1292    *
1293    * <p>
1294    * If <property>resolve</property> is <jk>true</jk>, the property object will automatically be  resolved by calling
1295    * {@link #resolve()}.
1296    * Therefore, <property>getProperty(name, <jk>true</jk>)</property> is equivalent to calling
1297    * <property>getProperty(name).resolve()</property>, except it's safe from a potential
1298    * <property>NullPointerException</property>.
1299    *
1300    * @param name The property name.  Can be <jk>null</jk> (no property has a <jk>null</jk> name, so <jk>null</jk> is returned).
1301    * @param resolve If <jk>true</jk>, calls {@link #resolve()} on object before returning.
1302    * @return The property with the specified name, or <jk>null</jk> if no property is specified.
1303    */
1304   public JsonSchema<?> getProperty(String name, boolean resolve) {
1305      if (properties == null)
1306         return null;
1307      var s = properties.get(name);
1308      if (s == null)
1309         return null;
1310      if (resolve)
1311         s = s.resolve();
1312      return s;
1313   }
1314
1315   /**
1316    * Bean property getter:  <property>readOnly</property>.
1317    *
1318    * <p>
1319    * This property was added in Draft 07.
1320    *
1321    * @return The value of the <property>readOnly</property> property on this bean, or <jk>null</jk> if it is not set.
1322    */
1323   public Boolean getReadOnly() { return readOnly; }
1324
1325   /**
1326    * Bean property getter:  <property>$ref</property>.
1327    *
1328    * @return The value of the <property>$ref</property> property on this bean, or <jk>null</jk> if it is not set.
1329    */
1330   @BeanProp("$ref")
1331   public URI getRef() { return ref; }
1332
1333   /**
1334    * Bean property getter:  <property>required</property>.
1335    *
1336    * @return The value of the <property>required</property> property on this bean, or <jk>null</jk> if it is not set.
1337    */
1338   public List<String> getRequired() { return u(required); }
1339
1340   /**
1341    * Bean property getter:  <property>$schema</property>.
1342    *
1343    * @return The value of the <property>$schema</property> property on this bean, or <jk>null</jk> if it is not set.
1344    */
1345   @BeanProp("$schema")
1346   public URI getSchemaVersionUri() { return schemaVersion; }
1347
1348   /**
1349    * Bean property getter:  <property>then</property>.
1350    *
1351    * <p>
1352    * This property was added in Draft 07 for conditional schema application.
1353    *
1354    * @return The value of the <property>then</property> property on this bean, or <jk>null</jk> if it is not set.
1355    */
1356   @BeanProp("then")
1357   public JsonSchema<?> getThen() { return then_; }
1358
1359   /**
1360    * Bean property getter:  <property>summary</property>.
1361    *
1362    * <p>
1363    * A short, concise summary of the schema's purpose, intended for AI/LLM consumption, compact
1364    * documentation, or any context where brevity is important. Unlike {@link #getDescription()},
1365    * which can be multi-line and detailed, this value should be a single sentence or phrase.
1366    *
1367    * <p>
1368    * This is a Juneau extension. It serializes as the JSON Schema keyword <c>"summary"</c>.
1369    *
1370    * @return The value of the <property>summary</property> property, or <jk>null</jk> if it is not set.
1371    * @since 10.0.0
1372    */
1373   public String getSummary() { return summary; }
1374
1375   /**
1376    * Bean property getter:  <property>title</property>.
1377    *
1378    * @return The value of the <property>title</property> property, or <jk>null</jk> if it is not set.
1379    */
1380   public String getTitle() { return title; }
1381
1382   /**
1383    * Bean property getter:  <property>type</property>.
1384    *
1385    * @return
1386    *    The value of the <property>type</property> property on this bean, or <jk>null</jk> if it is not set.
1387    *    Can be either a {@link JsonType} or {@link JsonTypeArray} depending on what value was used to set it.
1388    */
1389   @Swap(JsonTypeOrJsonTypeArraySwap.class)
1390   public Object getType() {
1391      if (nn(typeJsonType))
1392         return typeJsonType;
1393      return typeJsonTypeArray;
1394   }
1395
1396   /**
1397    * Bean property getter:  <property>type</property>.
1398    *
1399    * <p>
1400    * Convenience method for returning the <property>type</property> property when it is a {@link JsonType} value.
1401    *
1402    * @return
1403    *    The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonTypeArray}.
1404    */
1405   @BeanIgnore
1406   public JsonType getTypeAsJsonType() { return typeJsonType; }
1407
1408   /**
1409    * Bean property getter:  <property>type</property>.
1410    *
1411    * <p>
1412    * Convenience method for returning the <property>type</property> property when it is a {@link JsonTypeArray} value.
1413    *
1414    * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonType}.
1415    */
1416   @BeanIgnore
1417   public JsonTypeArray getTypeAsJsonTypeArray() { return typeJsonTypeArray; }
1418
1419   /**
1420    * Bean property getter:  <property>unevaluatedItems</property>.
1421    *
1422    * <p>
1423    * This property was added in Draft 2019-09.
1424    *
1425    * @return The value of the <property>unevaluatedItems</property> property on this bean, or <jk>null</jk> if it is not set.
1426    */
1427   public JsonSchema<?> getUnevaluatedItems() { return unevaluatedItems; }
1428
1429   /**
1430    * Bean property getter:  <property>unevaluatedProperties</property>.
1431    *
1432    * <p>
1433    * This property was added in Draft 2019-09.
1434    *
1435    * @return The value of the <property>unevaluatedProperties</property> property on this bean, or <jk>null</jk> if it is not set.
1436    */
1437   public JsonSchema<?> getUnevaluatedProperties() { return unevaluatedProperties; }
1438
1439   /**
1440    * Bean property getter:  <property>uniqueItems</property>.
1441    *
1442    * @return
1443    *    The value of the <property>uniqueItems</property> property on this bean, or <jk>null</jk> if it is not set.
1444    */
1445   public Boolean getUniqueItems() { return uniqueItems; }
1446
1447   /**
1448    * Bean property getter:  <property>writeOnly</property>.
1449    *
1450    * <p>
1451    * This property was added in Draft 07.
1452    *
1453    * @return The value of the <property>writeOnly</property> property on this bean, or <jk>null</jk> if it is not set.
1454    */
1455   public Boolean getWriteOnly() { return writeOnly; }
1456
1457   /**
1458    * Resolve schema if reference.
1459    *
1460    * <p>
1461    * If this schema is a reference to another schema (has its <property>$ref</property> property set), this
1462    * method will retrieve the referenced schema from the schema map registered with this schema.
1463    *
1464    * <p>
1465    * If this schema is not a reference, or no schema map is registered with this schema, this method is a no-op and
1466    * simply returns this object.
1467    *
1468    * @return The referenced schema, or <jk>null</jk> if this schema is a <property>$ref</property> whose target is not found in the registered schema map.
1469    */
1470   public JsonSchema<?> resolve() {
1471      if (ref == null || master.schemaMap == null)
1472         return this;
1473      return master.schemaMap.get(ref);
1474   }
1475
1476   /**
1477    * Bean property setter:  <property>additionalItems</property>.
1478    *
1479    * @param value
1480    *    The new value for the <property>additionalItems</property> property on this bean.
1481    *    This object must be of type {@link Boolean} or {@link JsonSchemaArray}.
1482    *    <br>Can be <jk>null</jk> to unset the property.
1483    * @return This object.
1484    * @throws BeanRuntimeException If invalid object type passed in.
1485    */
1486   public SELF setAdditionalItems(Object value) {
1487      this.additionalItemsBoolean = null;
1488      this.additionalItemsSchemaArray = null;
1489      if (nn(value)) {
1490         if (value instanceof Boolean value2)
1491            this.additionalItemsBoolean = value2;
1492         else if (value instanceof JsonSchemaArray value2) {
1493            this.additionalItemsSchemaArray = value2;
1494            setMasterOn(this.additionalItemsSchemaArray);
1495         } else {
1496            throw brex(JsonSchemaProperty.class, "Invalid attribute type '%s' passed in.  Must be one of the following:  Boolean, JsonSchemaArray", cn(value));
1497         }
1498      }
1499      return self();
1500   }
1501
1502   /**
1503    * Bean property setter:  <property>additionalProperties</property>.
1504    *
1505    * @param value
1506    *    The new value for the <property>additionalProperties</property> property on this bean.
1507    *    This object must be of type {@link Boolean} or {@link JsonSchema}.
1508    *    <br>Can be <jk>null</jk> to unset the property.
1509    * @return This object.
1510    * @throws BeanRuntimeException If invalid object type passed in.
1511    */
1512   @MarshalledProp(dictionary={ JsonSchema.class })
1513   public SELF setAdditionalProperties(Object value) {
1514      this.additionalPropertiesBoolean = null;
1515      this.additionalPropertiesSchema = null;
1516      if (nn(value)) {
1517         if (value instanceof Boolean value2)
1518            this.additionalPropertiesBoolean = value2;
1519         else if (value instanceof JsonSchema<?> value2) {
1520            this.additionalPropertiesSchema = value2;
1521            setMasterOn(this.additionalPropertiesSchema);
1522         } else
1523            throw brex(JsonSchemaProperty.class, "Invalid attribute type '%s' passed in.  Must be one of the following:  Boolean, JsonSchema", cn(value));
1524      }
1525      return self();
1526   }
1527
1528   /**
1529    * Bean property setter:  <property>allOf</property>.
1530    *
1531    * @param value The new value for the <property>allOf</property> property on this bean.
1532    * @return This object.
1533    */
1534   public SELF setAllOf(List<JsonSchema<?>> value) {
1535      this.allOf = value;
1536      setMasterOn(value);
1537      return self();
1538   }
1539
1540   /**
1541    * Bean property setter:  <property>allOf</property>.
1542    *
1543    * @param value The new value for the <property>allOf</property> property on this bean.
1544    * @return This object.
1545    */
1546   public SELF setAllOf(JsonSchema<?>...value) {
1547      return setAllOf(list(value));
1548   }
1549
1550   /**
1551    * Bean property setter:  <property>anyOf</property>.
1552    *
1553    * @param value The new value of the <property>anyOf</property> property on this bean.
1554    * @return This object.
1555    */
1556   public SELF setAnyOf(List<JsonSchema<?>> value) {
1557      this.anyOf = value;
1558      setMasterOn(value);
1559      return self();
1560   }
1561
1562   /**
1563    * Bean property setter:  <property>anyOf</property>.
1564    *
1565    * @param value The new value for the <property>anyOf</property> property on this bean.
1566    * @return This object.
1567    */
1568   public SELF setAnyOf(JsonSchema<?>...value) {
1569      return setAnyOf(list(value));
1570   }
1571
1572   /**
1573    * Bean property setter:  <property>const</property>.
1574    *
1575    * <p>
1576    * This property was added in Draft 06.
1577    *
1578    * @param value The new value for the <property>const</property> property on this bean.
1579    * @return This object.
1580    */
1581   public SELF setConst(Object value) {
1582      this.const_ = value;
1583      return self();
1584   }
1585
1586   /**
1587    * Bean property setter:  <property>contentEncoding</property>.
1588    *
1589    * <p>
1590    * This property was added in Draft 07.
1591    *
1592    * @param value The new value for the <property>contentEncoding</property> property on this bean.
1593    * @return This object.
1594    */
1595   public SELF setContentEncoding(String value) {
1596      this.contentEncoding = value;
1597      return self();
1598   }
1599
1600   /**
1601    * Bean property setter:  <property>contentMediaType</property>.
1602    *
1603    * <p>
1604    * This property was added in Draft 07.
1605    *
1606    * @param value The new value for the <property>contentMediaType</property> property on this bean.
1607    * @return This object.
1608    */
1609   public SELF setContentMediaType(String value) {
1610      this.contentMediaType = value;
1611      return self();
1612   }
1613
1614   /**
1615    * Bean property setter:  <property>deprecated</property>.
1616    *
1617    * <p>
1618    * This property was added in Draft 2019-09.
1619    *
1620    * @param value The new value for the <property>deprecated</property> property on this bean.
1621    * @return This object.
1622    */
1623   public SELF setDeprecated(Boolean value) {
1624      this.deprecated = value;
1625      return self();
1626   }
1627
1628   /**
1629    * Bean property setter:  <property>$comment</property>.
1630    *
1631    * <p>
1632    * This property was added in Draft 07.
1633    *
1634    * @param value The new value for the <property>$comment</property> property on this bean.
1635    * @return This object.
1636    */
1637   @BeanProp("$comment")
1638   public SELF setComment(String value) {
1639      this.comment = value;
1640      return self();
1641   }
1642
1643   /**
1644    * Bean property setter:  <property>definitions</property>.
1645    *
1646    * @param value The new value for the <property>definitions</property> property on this bean.
1647    * @return This object.
1648    */
1649   public SELF setDefinitions(Map<String,JsonSchema<?>> value) {
1650      this.definitions = value;
1651      if (nn(value))
1652         setMasterOn(value.values());
1653      return self();
1654   }
1655
1656   /**
1657    * Bean property setter:  <property>$defs</property>.
1658    *
1659    * <p>
1660    * This is the Draft 2020-12 replacement for <property>definitions</property>.
1661    * Both properties are supported for backward compatibility.
1662    *
1663    * @param value The new value for the <property>$defs</property> property on this bean.
1664    * @return This object.
1665    */
1666   @BeanProp("$defs")
1667   public SELF setDefs(Map<String,JsonSchema<?>> value) {
1668      this.defs = value;
1669      if (nn(value))
1670         setMasterOn(value.values());
1671      return self();
1672   }
1673
1674   /**
1675    * Bean property setter:  <property>dependencies</property>.
1676    *
1677    * @param value The new value for the <property>dependencies</property> property on this bean.
1678    * @return This object.
1679    */
1680   public SELF setDependencies(Map<String,JsonSchema<?>> value) {
1681      this.dependencies = value;
1682      if (nn(value))
1683         setMasterOn(value.values());
1684      return self();
1685   }
1686
1687   /**
1688    * Bean property setter:  <property>format</property>.
1689    *
1690    * @param value The new value for the <property>format</property> property on this bean.
1691    * @return This object.
1692    */
1693   public SELF setFormat(String value) {
1694      this.format = value;
1695      return self();
1696   }
1697
1698   /**
1699    * Bean property setter:  <property>dependentRequired</property>.
1700    *
1701    * <p>
1702    * This property was added in Draft 2019-09 as a replacement for the array form of <property>dependencies</property>.
1703    *
1704    * @param value The new value for the <property>dependentRequired</property> property on this bean.
1705    * @return This object.
1706    */
1707   public SELF setDependentRequired(Map<String,List<String>> value) {
1708      this.dependentRequired = value;
1709      return self();
1710   }
1711
1712   /**
1713    * Bean property setter:  <property>dependentSchemas</property>.
1714    *
1715    * <p>
1716    * This property was added in Draft 2019-09 as a replacement for the schema form of <property>dependencies</property>.
1717    *
1718    * @param value The new value for the <property>dependentSchemas</property> property on this bean.
1719    * @return This object.
1720    */
1721   public SELF setDependentSchemas(Map<String,JsonSchema<?>> value) {
1722      this.dependentSchemas = value;
1723      if (nn(value))
1724         setMasterOn(value.values());
1725      return self();
1726   }
1727
1728   /**
1729    * Bean property setter:  <property>description</property>.
1730    *
1731    * @param value The new value for the <property>description</property> property on this bean.
1732    * @return This object.
1733    */
1734   public SELF setDescription(String value) {
1735      this.description = value;
1736      return self();
1737   }
1738
1739   /**
1740    * Bean property setter:  <property>else</property>.
1741    *
1742    * <p>
1743    * This property was added in Draft 07 for conditional schema application.
1744    *
1745    * @param value The new value for the <property>else</property> property on this bean.
1746    * @return This object.
1747    */
1748   @BeanProp("else")
1749   public SELF setElse(JsonSchema<?> value) {
1750      this.else_ = value;
1751      setMasterOn(value);
1752      return self();
1753   }
1754
1755   /**
1756    * Bean property setter:  <property>enum</property>.
1757    *
1758    * @param value The new value for the <property>enum</property> property on this bean.
1759    * @return This object.
1760    */
1761   public SELF setEnum(List<Object> value) {
1762      this.enum_ = value;
1763      return self();
1764   }
1765
1766   /**
1767    * Bean property setter:  <property>enum</property>.
1768    *
1769    * @param value The new value for the <property>enum</property> property on this bean.
1770    * @return This object.
1771    */
1772   public SELF setEnum(Object...value) {
1773      return setEnum(list(value));
1774   }
1775
1776   /**
1777    * Bean property setter:  <property>examples</property>.
1778    *
1779    * <p>
1780    * This property was added in Draft 06.
1781    *
1782    * @param value The new value for the <property>examples</property> property on this bean.
1783    * @return This object.
1784    */
1785   public SELF setExamples(List<Object> value) {
1786      this.examples = value;
1787      return self();
1788   }
1789
1790   /**
1791    * Bean property setter:  <property>examples</property>.
1792    *
1793    * <p>
1794    * This property was added in Draft 06.
1795    *
1796    * @param value The new value for the <property>examples</property> property on this bean.
1797    * @return This object.
1798    */
1799   public SELF setExamples(Object...value) {
1800      return setExamples(list(value));
1801   }
1802
1803   /**
1804    * Bean property setter:  <property>exclusiveMaximum</property>.
1805    *
1806    * <p>
1807    * In Draft 06+, this is a numeric value representing the exclusive upper bound.
1808    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1809    *
1810    * @param value The new value for the <property>exclusiveMaximum</property> property on this bean.
1811    * @return This object.
1812    */
1813   public SELF setExclusiveMaximum(Number value) {
1814      this.exclusiveMaximum = value;
1815      return self();
1816   }
1817
1818   /**
1819    * Bean property setter:  <property>exclusiveMinimum</property>.
1820    *
1821    * <p>
1822    * In Draft 06+, this is a numeric value representing the exclusive lower bound.
1823    * In Draft 04, this was a boolean flag. This implementation uses the Draft 06+ semantics.
1824    *
1825    * @param value The new value for the <property>exclusiveMinimum</property> property on this bean.
1826    * @return This object.
1827    */
1828   public SELF setExclusiveMinimum(Number value) {
1829      this.exclusiveMinimum = value;
1830      return self();
1831   }
1832
1833   /**
1834    * Bean property setter:  <property>id</property>.
1835    *
1836    * <p>
1837    * <b>Deprecated:</b> Use {@link #setIdUri(Object)} instead.
1838    * This property is retained for Draft 04 backward compatibility.
1839    *
1840    * <p>
1841    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1842    * Strings must be valid URIs.
1843    *
1844    * <p>
1845    * URIs defined by {@link UriResolver} can be used for values.
1846    *
1847    * @param value The new value for the <property>id</property> property on this bean.  Can be <jk>null</jk> to unset the property.
1848    * @return This object.
1849    * @deprecated Use {@link #setIdUri(Object)} instead.
1850    */
1851   @Deprecated(since = "10.0", forRemoval = true)
1852   public SELF setId(Object value) {
1853      this.id = toUri(value);
1854      return self();
1855   }
1856
1857   /**
1858    * Bean property setter:  <property>$id</property>.
1859    *
1860    * <p>
1861    * This is the Draft 2020-12 property for schema identification.
1862    *
1863    * <p>
1864    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
1865    * Strings must be valid URIs.
1866    *
1867    * <p>
1868    * URIs defined by {@link UriResolver} can be used for values.
1869    *
1870    * @param value The new value for the <property>$id</property> property on this bean.  Can be <jk>null</jk> to unset the property.
1871    * @return This object.
1872    */
1873   @BeanProp("$id")
1874   public SELF setIdUri(Object value) {
1875      this.idUri = toUri(value);
1876      return self();
1877   }
1878
1879   /**
1880    * Bean property setter:  <property>if</property>.
1881    *
1882    * <p>
1883    * This property was added in Draft 07 for conditional schema application.
1884    *
1885    * @param value The new value for the <property>if</property> property on this bean.
1886    * @return This object.
1887    */
1888   @BeanProp("if")
1889   public SELF setIf(JsonSchema<?> value) {
1890      this.if_ = value;
1891      setMasterOn(value);
1892      return self();
1893   }
1894
1895   /**
1896    * Bean property setter:  <property>items</property>.
1897    *
1898    * @param value
1899    *    The new value for the <property>items</property> property on this bean.
1900    *    This object must be of type {@link JsonSchema} or {@link JsonSchemaArray}.
1901    *    <br>Can be <jk>null</jk> to unset the property.
1902    * @return This object.
1903    * @throws BeanRuntimeException If invalid object type passed in.
1904    */
1905   public SELF setItems(Object value) {
1906      this.itemsSchema = null;
1907      this.itemsSchemaArray = null;
1908      if (nn(value)) {
1909         if (value instanceof JsonSchema<?> value2) {
1910            this.itemsSchema = value2;
1911            setMasterOn(this.itemsSchema);
1912         } else if (value instanceof JsonSchemaArray value2) {
1913            this.itemsSchemaArray = value2;
1914            setMasterOn(this.itemsSchemaArray);
1915         } else {
1916            throw brex(JsonSchemaProperty.class, "Invalid attribute type '%s' passed in.  Must be one of the following:  JsonSchema, JsonSchemaArray", cn(value));
1917         }
1918      }
1919      return self();
1920   }
1921
1922   /**
1923    * Bean property setter:  <property>maximum</property>.
1924    *
1925    * @param value The new value for the <property>maximum</property> property on this bean.
1926    * @return This object.
1927    */
1928   public SELF setMaximum(Number value) {
1929      this.maximum = value;
1930      return self();
1931   }
1932
1933   /**
1934    * Bean property setter:  <property>maxItems</property>.
1935    *
1936    * @param value The new value for the <property>maxItems</property> property on this bean.
1937    * @return This object.
1938    */
1939   public SELF setMaxItems(Integer value) {
1940      this.maxItems = value;
1941      return self();
1942   }
1943
1944   /**
1945    * Bean property setter:  <property>maxLength</property>.
1946    *
1947    * @param value The new value for the <property>maxLength</property> property on this bean.
1948    * @return This object.
1949    */
1950   public SELF setMaxLength(Integer value) {
1951      this.maxLength = value;
1952      return self();
1953   }
1954
1955   /**
1956    * Bean property setter:  <property>maxProperties</property>.
1957    *
1958    * @param value The new value for the <property>maxProperties</property> property on this bean.
1959    * @return This object.
1960    */
1961   public SELF setMaxProperties(Integer value) {
1962      this.maxProperties = value;
1963      return self();
1964   }
1965
1966   /**
1967    * Bean property setter:  <property>minimum</property>.
1968    *
1969    * @param value The new value for the <property>minimum</property> property on this bean.
1970    * @return This object.
1971    */
1972   public SELF setMinimum(Number value) {
1973      this.minimum = value;
1974      return self();
1975   }
1976
1977   /**
1978    * Bean property setter:  <property>minItems</property>.
1979    *
1980    * @param value The new value for the <property>minItems</property> property on this bean.
1981    * @return This object.
1982    */
1983   public SELF setMinItems(Integer value) {
1984      this.minItems = value;
1985      return self();
1986   }
1987
1988   /**
1989    * Bean property setter:  <property>minLength</property>.
1990    *
1991    * @param value The new value for the <property>minLength</property> property on this bean.
1992    * @return This object.
1993    */
1994   public SELF setMinLength(Integer value) {
1995      this.minLength = value;
1996      return self();
1997   }
1998
1999   /**
2000    * Bean property setter:  <property>minProperties</property>.
2001    *
2002    * @param value The new value for the <property>minProperties</property> property on this bean.
2003    * @return This object.
2004    */
2005   public SELF setMinProperties(Integer value) {
2006      this.minProperties = value;
2007      return self();
2008   }
2009
2010   /**
2011    * Bean property setter:  <property>multipleOf</property>.
2012    *
2013    * @param value The new value for the <property>multipleOf</property> property on this bean.
2014    * @return This object.
2015    */
2016   public SELF setMultipleOf(Number value) {
2017      this.multipleOf = value;
2018      return self();
2019   }
2020
2021   /**
2022    * Bean property setter:  <property>name</property>.
2023    *
2024    * <p>
2025    * This is an internal property used for tracking property names and is not part of the JSON Schema specification.
2026    *
2027    * @param value The new value for the <property>name</property> property on this bean.
2028    * @return This object.
2029    */
2030   @BeanIgnore
2031   public SELF setName(String value) {
2032      this.name = value;
2033      return self();
2034   }
2035
2036   /**
2037    * Bean property setter:  <property>not</property>.
2038    *
2039    * @param value The new value for the <property>not</property> property on this bean.
2040    * @return This object.
2041    */
2042   public SELF setNot(JsonSchema<?> value) {
2043      this.not = value;
2044      setMasterOn(value);
2045      return self();
2046   }
2047
2048   /**
2049    * Bean property setter:  <property>oneOf</property>.
2050    *
2051    * @param value The new value for the <property>oneOf</property> property on this bean.
2052    * @return This object.
2053    */
2054   public SELF setOneOf(List<JsonSchema<?>> value) {
2055      this.oneOf = value;
2056      setMasterOn(value);
2057      return self();
2058   }
2059
2060   /**
2061    * Bean property setter:  <property>oneOf</property>.
2062    *
2063    * @param value The new value for the <property>oneOf</property> property on this bean.
2064    * @return This object.
2065    */
2066   public SELF setOneOf(JsonSchema<?>...value) {
2067      return setOneOf(list(value));
2068   }
2069
2070   /**
2071    * Bean property setter:  <property>pattern</property>.
2072    *
2073    * @param value The new value for the <property>pattern</property> property on this bean.
2074    * @return This object.
2075    */
2076   public SELF setPattern(String value) {
2077      this.pattern = value;
2078      return self();
2079   }
2080
2081   /**
2082    * Bean property setter:  <property>patternProperties</property>.
2083    *
2084    * @param value The new value for the <property>patternProperties</property> property on this bean.
2085    * @return This object.
2086    */
2087   public SELF setPatternProperties(Map<String,JsonSchema<?>> value) {
2088      this.patternProperties = value;
2089      if (nn(value)) {
2090         value.entrySet().forEach(x -> {
2091            var s = x.getValue();
2092            setMasterOn(s);
2093            s.setName(x.getKey());
2094         });
2095      }
2096      return self();
2097   }
2098
2099   /**
2100    * Bean property setter:  <property>prefixItems</property>.
2101    *
2102    * <p>
2103    * This property was added in Draft 2020-12 for tuple validation.
2104    *
2105    * @param value The new value for the <property>prefixItems</property> property on this bean.
2106    * @return This object.
2107    */
2108   public SELF setPrefixItems(JsonSchemaArray value) {
2109      this.prefixItems = value;
2110      setMasterOn(value);
2111      return self();
2112   }
2113
2114   /**
2115    * Bean property setter:  <property>properties</property>.
2116    *
2117    * @param value The new value for the <property>properties</property> property on this bean.
2118    * @return This object.
2119    */
2120   public SELF setProperties(Map<String,JsonSchema<?>> value) {
2121      this.properties = value;
2122      if (nn(value)) {
2123         value.entrySet().forEach(x -> {
2124            var v = x.getValue();
2125            setMasterOn(v);
2126            v.setName(x.getKey());
2127         });
2128      }
2129      return self();
2130   }
2131
2132   /**
2133    * Bean property setter:  <property>readOnly</property>.
2134    *
2135    * <p>
2136    * This property was added in Draft 07.
2137    *
2138    * @param value The new value for the <property>readOnly</property> property on this bean.
2139    * @return This object.
2140    */
2141   public SELF setReadOnly(Boolean value) {
2142      this.readOnly = value;
2143      return self();
2144   }
2145
2146   /**
2147    * Bean property setter:  <property>$ref</property>.
2148    *
2149    * <p>
2150    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
2151    * Strings must be valid URIs.
2152    *
2153    * <p>
2154    * URIs defined by {@link UriResolver} can be used for values.
2155    *
2156    * @param value The new value for the <property>$ref</property> property on this bean.  Can be <jk>null</jk> to unset the property.
2157    * @return This object.
2158    */
2159   @BeanProp("$ref")
2160   public SELF setRef(Object value) {
2161      this.ref = toUri(value);
2162      return self();
2163   }
2164
2165   /**
2166    * Bean property setter:  <property>required</property>.
2167    *
2168    * @param value The new value for the <property>required</property> property on this bean.
2169    * @return This object.
2170    */
2171   public SELF setRequired(List<String> value) {
2172      this.required = value;
2173      return self();
2174   }
2175
2176   /**
2177    * Bean property setter:  <property>required</property>.
2178    *
2179    * @param value The new value for the <property>required</property> property on this bean.
2180    * @return This object.
2181    */
2182   public SELF setRequired(String...value) {
2183      return setRequired(list(value));
2184   }
2185
2186   /**
2187    * Associates a schema map with this schema for resolving other schemas identified through <property>$ref</property>
2188    * properties.
2189    *
2190    * @param value The schema map to associate with this schema.  Can be <jk>null</jk> to unset the property.
2191    * @return This object.
2192    */
2193   @BeanIgnore
2194   public SELF setSchemaMap(JsonSchemaMap value) {
2195      this.schemaMap = value;
2196      return self();
2197   }
2198
2199   /**
2200    * Bean property setter:  <property>$schema</property>.
2201    *
2202    * <p>
2203    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
2204    * Strings must be valid URIs.
2205    *
2206    * <p>
2207    * URIs defined by {@link UriResolver} can be used for values.
2208    *
2209    * @param value The new value for the <property>schemaVersion</property> property on this bean.  Can be <jk>null</jk> to unset the property.
2210    * @return This object.
2211    */
2212   @BeanProp("$schema")
2213   public SELF setSchemaVersionUri(Object value) {
2214      this.schemaVersion = toUri(value);
2215      return self();
2216   }
2217
2218   /**
2219    * Bean property setter:  <property>then</property>.
2220    *
2221    * <p>
2222    * This property was added in Draft 07 for conditional schema application.
2223    *
2224    * @param value The new value for the <property>then</property> property on this bean.
2225    * @return This object.
2226    */
2227   @BeanProp("then")
2228   public SELF setThen(JsonSchema<?> value) {
2229      this.then_ = value;
2230      setMasterOn(value);
2231      return self();
2232   }
2233
2234   /**
2235    * Bean property setter:  <property>summary</property>.
2236    *
2237    * @param value The new value for the <property>summary</property> property on this bean.
2238    * @return This object.
2239    * @since 10.0.0
2240    */
2241   public SELF setSummary(String value) {
2242      this.summary = value;
2243      return self();
2244   }
2245
2246   /**
2247    * Bean property setter:  <property>title</property>.
2248    *
2249    * @param value The new value for the <property>title</property> property on this bean.
2250    * @return This object.
2251    */
2252   public SELF setTitle(String value) {
2253      this.title = value;
2254      return self();
2255   }
2256
2257   /**
2258    * Bean property setter:  <property>type</property>.
2259    *
2260    * @param value
2261    *    The new value for the <property>type</property> property on this bean.
2262    *    This object must be of type {@link JsonType} or {@link JsonTypeArray}.
2263    *    <br>Can be <jk>null</jk> to unset the property.
2264    * @return This object.
2265    * @throws BeanRuntimeException If invalid object type passed in.
2266    */
2267   public SELF setType(Object value) {
2268      this.typeJsonType = null;
2269      this.typeJsonTypeArray = null;
2270      if (nn(value)) {
2271         if (value instanceof JsonType value2)
2272            this.typeJsonType = value2;
2273         else if (value instanceof JsonTypeArray value2)
2274            this.typeJsonTypeArray = value2;
2275         else
2276            throw brex(JsonSchemaProperty.class, "Invalid attribute type '%s' passed in.  Must be one of the following:  SimpleType, SimpleTypeArray", cn(value));
2277      }
2278      return self();
2279   }
2280
2281   /**
2282    * Bean property setter:  <property>unevaluatedItems</property>.
2283    *
2284    * <p>
2285    * This property was added in Draft 2019-09.
2286    *
2287    * @param value The new value for the <property>unevaluatedItems</property> property on this bean.
2288    * @return This object.
2289    */
2290   public SELF setUnevaluatedItems(JsonSchema<?> value) {
2291      this.unevaluatedItems = value;
2292      setMasterOn(value);
2293      return self();
2294   }
2295
2296   /**
2297    * Bean property setter:  <property>unevaluatedProperties</property>.
2298    *
2299    * <p>
2300    * This property was added in Draft 2019-09.
2301    *
2302    * @param value The new value for the <property>unevaluatedProperties</property> property on this bean.
2303    * @return This object.
2304    */
2305   public SELF setUnevaluatedProperties(JsonSchema<?> value) {
2306      this.unevaluatedProperties = value;
2307      setMasterOn(value);
2308      return self();
2309   }
2310
2311   /**
2312    * Bean property setter:  <property>uniqueItems</property>.
2313    *
2314    * @param value The new value for the <property>uniqueItems</property> property on this bean.
2315    * @return This object.
2316    */
2317   public SELF setUniqueItems(Boolean value) {
2318      this.uniqueItems = value;
2319      return self();
2320   }
2321
2322   /**
2323    * Bean property setter:  <property>writeOnly</property>.
2324    *
2325    * <p>
2326    * This property was added in Draft 07.
2327    *
2328    * @param value The new value for the <property>writeOnly</property> property on this bean.
2329    * @return This object.
2330    */
2331   public SELF setWriteOnly(Boolean value) {
2332      this.writeOnly = value;
2333      return self();
2334   }
2335
2336   @Override /* Overridden from Object */
2337   public String toString() {
2338      return Json.of(this);
2339   }
2340
2341   private void setMasterOn(Collection<JsonSchema<?>> ss) {
2342      if (nn(ss))
2343         ss.forEach(this::setMasterOn);
2344   }
2345
2346   private void setMasterOn(JsonSchema<?> s) {
2347      if (nn(s))
2348         s.setMaster(this);
2349   }
2350
2351   private void setMasterOn(JsonSchema<?>[] ss) {
2352      reqnn("ss", ss);
2353      for (var s : ss)
2354         setMasterOn(s);
2355   }
2356
2357   private void setMasterOn(JsonSchemaArray ss) {
2358      if (nn(ss))
2359         ss.forEach(this::setMasterOn);
2360   }
2361
2362   /**
2363    * Sets the master schema for this schema and all child schema objects.
2364    *
2365    * <p>
2366    * All child elements in a schema should point to a single "master" schema in order to locate registered JsonSchemaMap
2367    * objects for resolving external schemas.
2368    *
2369    * @param master The master schema to associate on this and all children.  Can be <jk>null</jk> to clear the master reference on this schema and all children.
2370    */
2371   @SuppressWarnings({
2372      "java:S3776" // Cognitive complexity acceptable for this logic
2373   })
2374   protected void setMaster(JsonSchema<?> master) {
2375      this.master = master;
2376      if (nn(definitions))
2377         definitions.values().forEach(x -> x.setMaster(master));
2378      if (nn(defs))
2379         defs.values().forEach(x -> x.setMaster(master));
2380      if (nn(properties))
2381         properties.values().forEach(x -> x.setMaster(master));
2382      if (nn(patternProperties))
2383         patternProperties.values().forEach(x -> x.setMaster(master));
2384      if (nn(dependencies))
2385         dependencies.values().forEach(x -> x.setMaster(master));
2386      if (nn(dependentSchemas))
2387         dependentSchemas.values().forEach(x -> x.setMaster(master));
2388      if (nn(itemsSchema))
2389         itemsSchema.setMaster(master);
2390      if (nn(itemsSchemaArray))
2391         itemsSchemaArray.forEach(x -> x.setMaster(master));
2392      if (nn(prefixItems))
2393         prefixItems.forEach(x -> x.setMaster(master));
2394      if (nn(additionalItemsSchemaArray))
2395         additionalItemsSchemaArray.forEach(x -> x.setMaster(master));
2396      if (nn(unevaluatedItems))
2397         unevaluatedItems.setMaster(master);
2398      if (nn(additionalPropertiesSchema))
2399         additionalPropertiesSchema.setMaster(master);
2400      if (nn(unevaluatedProperties))
2401         unevaluatedProperties.setMaster(master);
2402      if (nn(allOf))
2403         allOf.forEach(x -> x.setMaster(master));
2404      if (nn(anyOf))
2405         anyOf.forEach(x -> x.setMaster(master));
2406      if (nn(oneOf))
2407         oneOf.forEach(x -> x.setMaster(master));
2408      if (nn(not))
2409         not.setMaster(master);
2410      if (nn(if_))
2411         if_.setMaster(master);
2412      if (nn(then_))
2413         then_.setMaster(master);
2414      if (nn(else_))
2415         else_.setMaster(master);
2416   }
2417}