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