001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.dto.jsonschema; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016import static org.apache.juneau.internal.ClassUtils.*; 017import static org.apache.juneau.internal.CollectionUtils.*; 018 019import java.net.*; 020import java.util.*; 021 022import org.apache.juneau.*; 023import org.apache.juneau.annotation.*; 024import org.apache.juneau.json.*; 025import org.apache.juneau.parser.*; 026import org.apache.juneau.serializer.*; 027import org.apache.juneau.swap.*; 028 029/** 030 * Represents a top-level schema object bean in the JSON-Schema core specification. 031 */ 032@Bean(typeName="schema", 033 properties="id,$schema,$ref, title,description,type,definitions,properties," 034 + "patternProperties,dependencies,items,multipleOf,maximum,exclusiveMaximum," 035 + "minimum,exclusiveMinimum,maxLength,minLength,pattern,additionalItems," 036 + "maxItems,minItems,uniqueItems,maxProperties,minProperties,required," 037 + "additionalProperties,enum,allOf,anyOf,oneOf,not" 038) 039public class JsonSchema { 040 private String name; // Property name. Not serialized. 041 private URI id; 042 private URI schemaVersion; 043 private String title; 044 private String description; 045 private JsonType typeJsonType; // JsonType representation of type 046 private JsonTypeArray typeJsonTypeArray; // JsonTypeArray representation of type 047 private Map<String,JsonSchema> definitions; 048 private Map<String,JsonSchema> properties; 049 private Map<String,JsonSchema> patternProperties; 050 private Map<String,JsonSchema> dependencies; 051 private JsonSchema itemsSchema; // JsonSchema representation of items 052 private JsonSchemaArray itemsSchemaArray; // JsonSchemaArray representation of items 053 private Number multipleOf; 054 private Number maximum; 055 private Boolean exclusiveMaximum; 056 private Number minimum; 057 private Boolean exclusiveMinimum; 058 private Integer maxLength; 059 private Integer minLength; 060 private String pattern; 061 private Boolean additionalItemsBoolean; // Boolean representation of additionalItems 062 private JsonSchemaArray additionalItemsSchemaArray; // JsonSchemaArray representation of additionalItems 063 private Integer maxItems; 064 private Integer minItems; 065 private Boolean uniqueItems; 066 private Integer maxProperties; 067 private Integer minProperties; 068 private List<String> required; 069 private Boolean additionalPropertiesBoolean; // Boolean representation of additionalProperties 070 private JsonSchema additionalPropertiesSchema; // JsonSchema representation of additionalProperties 071 private List<String> _enum; 072 private List<JsonSchema> allOf; 073 private List<JsonSchema> anyOf; 074 private List<JsonSchema> oneOf; 075 private JsonSchema not; 076 private URI ref; 077 private JsonSchemaMap schemaMap; 078 private JsonSchema master = this; 079 080 /** 081 * Default constructor. 082 */ 083 public JsonSchema() {} 084 085 086 //----------------------------------------------------------------------------------------------------------------- 087 // Bean properties 088 //----------------------------------------------------------------------------------------------------------------- 089 090 /** 091 * Bean property getter: <property>name</property>. 092 * 093 * @return The value of the <property>name</property> property on this bean, or <jk>null</jk> if it is not set. 094 */ 095 public String getName() { 096 return name; 097 } 098 099 /** 100 * Bean property setter: <property>name</property>. 101 * 102 * @param name The new value for the <property>name</property> property on this bean. 103 * @return This object. 104 */ 105 public JsonSchema setName(String name) { 106 this.name = name; 107 return this; 108 } 109 110 /** 111 * Bean property getter: <property>id</property>. 112 * 113 * @return The value of the <property>id</property> property on this bean, or <jk>null</jk> if it is not set. 114 */ 115 public URI getId() { 116 return id; 117 } 118 119 /** 120 * Bean property setter: <property>id</property>. 121 * 122 * <p> 123 * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}. 124 * Strings must be valid URIs. 125 * 126 * <p> 127 * URIs defined by {@link UriResolver} can be used for values. 128 * 129 * @param id The new value for the <property>id</property> property on this bean. 130 * @return This object. 131 */ 132 public JsonSchema setId(Object id) { 133 this.id = toURI(id); 134 return this; 135 } 136 137 /** 138 * Bean property getter: <property>$schema</property>. 139 * 140 * @return The value of the <property>$schema</property> property on this bean, or <jk>null</jk> if it is not set. 141 */ 142 @Beanp("$schema") 143 public URI getSchemaVersionUri() { 144 return schemaVersion; 145 } 146 147 /** 148 * Bean property setter: <property>$schema</property>. 149 * 150 * <p> 151 * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}. 152 * Strings must be valid URIs. 153 * 154 * <p> 155 * URIs defined by {@link UriResolver} can be used for values. 156 * 157 * @param schemaVersion The new value for the <property>schemaVersion</property> property on this bean. 158 * @return This object. 159 */ 160 @Beanp("$schema") 161 public JsonSchema setSchemaVersionUri(Object schemaVersion) { 162 this.schemaVersion = toURI(schemaVersion); 163 return this; 164 } 165 166 /** 167 * Bean property getter: <property>title</property>. 168 * 169 * @return The value of the <property>title</property> property, or <jk>null</jk> if it is not set. 170 */ 171 public String getTitle() { 172 return title; 173 } 174 175 /** 176 * Bean property setter: <property>title</property>. 177 * 178 * @param title The new value for the <property>title</property> property on this bean. 179 * @return This object. 180 */ 181 public JsonSchema setTitle(String title) { 182 this.title = title; 183 return this; 184 } 185 186 /** 187 * Bean property getter: <property>description</property>. 188 * 189 * @return The value of the <property>description</property> property, or <jk>null</jk> if it is not set. 190 */ 191 public String getDescription() { 192 return description; 193 } 194 195 /** 196 * Bean property setter: <property>description</property>. 197 * 198 * @param description The new value for the <property>description</property> property on this bean. 199 * @return This object. 200 */ 201 public JsonSchema setDescription(String description) { 202 this.description = description; 203 return this; 204 } 205 206 /** 207 * Bean property getter: <property>type</property>. 208 * 209 * @return 210 * The value of the <property>type</property> property on this bean, or <jk>null</jk> if it is not set. 211 * Can be either a {@link JsonType} or {@link JsonTypeArray} depending on what value was used to set it. 212 */ 213 @Swap(JsonTypeOrJsonTypeArraySwap.class) 214 public Object getType() { 215 if (typeJsonType != null) 216 return typeJsonType; 217 return typeJsonTypeArray; 218 } 219 220 /** 221 * Bean property getter: <property>type</property>. 222 * 223 * <p> 224 * Convenience method for returning the <property>type</property> property when it is a {@link JsonType} value. 225 * 226 * @return 227 * The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonTypeArray}. 228 */ 229 public JsonType getTypeAsJsonType() { 230 return typeJsonType; 231 } 232 233 /** 234 * Bean property getter: <property>type</property>. 235 * 236 * <p> 237 * Convenience method for returning the <property>type</property> property when it is a {@link JsonTypeArray} value. 238 * 239 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonType}. 240 */ 241 public JsonTypeArray getTypeAsJsonTypeArray() { 242 return typeJsonTypeArray; 243 } 244 245 /** 246 * Bean property setter: <property>type</property>. 247 * 248 * @param type 249 * The new value for the <property>type</property> property on this bean. 250 * This object must be of type {@link JsonType} or {@link JsonTypeArray}. 251 * @return This object. 252 * @throws BeanRuntimeException If invalid object type passed in. 253 */ 254 public JsonSchema setType(Object type) { 255 this.typeJsonType = null; 256 this.typeJsonTypeArray = null; 257 if (type != null) { 258 if (type instanceof JsonType) 259 this.typeJsonType = (JsonType)type; 260 else if (type instanceof JsonTypeArray) 261 this.typeJsonTypeArray = (JsonTypeArray)type; 262 else 263 throw new BeanRuntimeException(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in. Must be one of the following: SimpleType, SimpleTypeArray", className(type)); 264 } 265 return this; 266 } 267 268 /** 269 * Bean property appender: <property>type</property>. 270 * 271 * @param types The list of items to append to the <property>type</property> property on this bean. 272 * @return This object. 273 */ 274 public JsonSchema addTypes(JsonType...types) { 275 if (this.typeJsonTypeArray == null) 276 this.typeJsonTypeArray = new JsonTypeArray(); 277 this.typeJsonTypeArray.addAll(types); 278 return this; 279 } 280 281 /** 282 * Used during parsing to convert the <property>type</property> property to the correct class type. 283 * 284 * <ul class='spaced-list'> 285 * <li> 286 * If parsing a JSON-array, converts to a {@link JsonTypeArray}. 287 * <li> 288 * If parsing a JSON-object, converts to a {@link JsonType}. 289 * </ul> 290 * 291 * <p> 292 * Serialization method is a no-op. 293 */ 294 public static class JsonTypeOrJsonTypeArraySwap extends ObjectSwap<Object,Object> { 295 296 @Override /* ObjectSwap */ 297 public Object swap(BeanSession session, Object o) throws SerializeException { 298 return o; 299 } 300 301 @Override /* ObjectSwap */ 302 public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException { 303 ClassMeta<?> cm = ( 304 o instanceof Collection 305 ? session.getClassMeta(JsonTypeArray.class) 306 : session.getClassMeta(JsonType.class) 307 ); 308 return session.convertToType(o, cm); 309 } 310 } 311 312 /** 313 * Bean property getter: <property>definitions</property>. 314 * 315 * @return 316 * The value of the <property>definitions</property> property on this bean, or <jk>null</jk> if it is not set. 317 */ 318 public Map<String,JsonSchema> getDefinitions() { 319 return definitions; 320 } 321 322 /** 323 * Bean property setter: <property>definitions</property>. 324 * 325 * @param definitions The new value for the <property>definitions</property> property on this bean. 326 * @return This object. 327 */ 328 public JsonSchema setDefinitions(Map<String,JsonSchema> definitions) { 329 this.definitions = definitions; 330 if (definitions != null) 331 setMasterOn(definitions.values()); 332 return this; 333 } 334 335 /** 336 * Bean property appender: <property>definitions</property>. 337 * 338 * @param name The key in the definitions map entry. 339 * @param definition The value in the definitions map entry. 340 * @return This object. 341 */ 342 public JsonSchema addDefinition(String name, JsonSchema definition) { 343 if (this.definitions == null) 344 this.definitions = map(); 345 this.definitions.put(name, definition); 346 setMasterOn(definition); 347 return this; 348 } 349 350 /** 351 * Bean property getter: <property>properties</property>. 352 * 353 * @return The value of the <property>properties</property> property on this bean, or <jk>null</jk> if it is not set. 354 */ 355 public Map<String,JsonSchema> getProperties() { 356 return properties; 357 } 358 359 /** 360 * Returns the property with the specified name. 361 * 362 * <p> 363 * This is equivalent to calling <property>getProperty(name, <jk>false</jk>)</property>. 364 * 365 * @param name The property name. 366 * @return The property with the specified name, or <jk>null</jk> if no property is specified. 367 */ 368 public JsonSchema getProperty(String name) { 369 return getProperty(name, false); 370 } 371 372 /** 373 * Returns the property with the specified name. 374 * 375 * <p> 376 * If <property>resolve</property> is <jk>true</jk>, the property object will automatically be resolved by calling 377 * {@link #resolve()}. 378 * Therefore, <property>getProperty(name, <jk>true</jk>)</property> is equivalent to calling 379 * <property>getProperty(name).resolve()</property>, except it's safe from a potential 380 * <property>NullPointerException</property>. 381 * 382 * @param name The property name. 383 * @param resolve If <jk>true</jk>, calls {@link #resolve()} on object before returning. 384 * @return The property with the specified name, or <jk>null</jk> if no property is specified. 385 */ 386 public JsonSchema getProperty(String name, boolean resolve) { 387 if (properties == null) 388 return null; 389 JsonSchema s = properties.get(name); 390 if (s == null) 391 return null; 392 if (resolve) 393 s = s.resolve(); 394 return s; 395 } 396 397 /** 398 * Bean property setter: <property>properties</property>. 399 * 400 * @param properties The new value for the <property>properties</property> property on this bean. 401 * @return This object. 402 */ 403 public JsonSchema setProperties(Map<String,JsonSchema> properties) { 404 this.properties = properties; 405 if (properties != null) { 406 properties.entrySet().forEach(x -> { 407 JsonSchema value = x.getValue(); 408 setMasterOn(value); 409 value.setName(x.getKey()); 410 }); 411 } 412 return this; 413 } 414 415 /** 416 * Bean property appender: <property>properties</property>. 417 * 418 * <p> 419 * Properties must have their <property>name</property> property set on them when using this method. 420 * 421 * @param properties The list of items to append to the <property>properties</property> property on this bean. 422 * @return This object. 423 * @throws BeanRuntimeException If property is found without a set <property>name</property> property. 424 */ 425 public JsonSchema addProperties(JsonSchema...properties) { 426 if (this.properties == null) 427 this.properties = map(); 428 for (JsonSchema p : properties) { 429 if (p.getName() == null) 430 throw new BeanRuntimeException(JsonSchema.class, 431 "Invalid property passed to JsonSchema.addProperties(). Property name was null."); 432 setMasterOn(p); 433 this.properties.put(p.getName(), p); 434 } 435 return this; 436 } 437 438 /** 439 * Bean property getter: <property>patternProperties</property>. 440 * 441 * @return 442 * The value of the <property>patternProperties</property> property on this bean, or <jk>null</jk> if it is 443 * not set. 444 */ 445 public Map<String,JsonSchema> getPatternProperties() { 446 return patternProperties; 447 } 448 449 /** 450 * Bean property setter: <property>patternProperties</property>. 451 * 452 * @param patternProperties The new value for the <property>patternProperties</property> property on this bean. 453 * @return This object. 454 */ 455 public JsonSchema setPatternProperties(Map<String,JsonSchema> patternProperties) { 456 this.patternProperties = patternProperties; 457 if (patternProperties != null) { 458 patternProperties.entrySet().forEach(x -> { 459 JsonSchema s = x.getValue(); 460 setMasterOn(s); 461 s.setName(x.getKey()); 462 }); 463 } 464 return this; 465 } 466 467 /** 468 * Bean property appender: <property>patternProperties</property>. 469 * 470 * <p> 471 * Properties must have their <property>name</property> property set to the pattern string when using this method. 472 * 473 * @param properties The list of items to append to the <property>patternProperties</property> property on this bean. 474 * @return This object. 475 * @throws BeanRuntimeException If property is found without a set <property>name</property> property. 476 */ 477 public JsonSchema addPatternProperties(JsonSchemaProperty...properties) { 478 if (this.patternProperties == null) 479 this.patternProperties = map(); 480 for (JsonSchema p : properties) { 481 if (p.getName() == null) 482 throw new BeanRuntimeException(JsonSchema.class, 483 "Invalid property passed to JsonSchema.addProperties(). Property name was null."); 484 setMasterOn(p); 485 this.patternProperties.put(p.getName(), p); 486 } 487 return this; 488 } 489 490 /** 491 * Bean property getter: <property>dependencies</property>. 492 * 493 * @return 494 * The value of the <property>dependencies</property> property on this bean, or <jk>null</jk> if it is not set. 495 */ 496 public Map<String,JsonSchema> getDependencies() { 497 return dependencies; 498 } 499 500 /** 501 * Bean property setter: <property>dependencies</property>. 502 * 503 * @param dependencies The new value for the <property>dependencies</property> property on this bean. 504 * @return This object. 505 */ 506 public JsonSchema setDependencies(Map<String,JsonSchema> dependencies) { 507 this.dependencies = dependencies; 508 if (dependencies != null) 509 setMasterOn(dependencies.values()); 510 return this; 511 } 512 513 /** 514 * Bean property appender: <property>dependencies</property>. 515 * 516 * @param name The key of the entry in the dependencies map. 517 * @param dependency The value of the entry in the dependencies map. 518 * @return This object. 519 */ 520 public JsonSchema addDependency(String name, JsonSchema dependency) { 521 if (this.dependencies == null) 522 this.dependencies = map(); 523 this.dependencies.put(name, dependency); 524 setMasterOn(dependency); 525 return this; 526 } 527 528 /** 529 * Bean property getter: <property>items</property>. 530 * 531 * @return 532 * The value of the <property>items</property> property on this bean, or <jk>null</jk> if it is not set. 533 * Can be either a {@link JsonSchema} or {@link JsonSchemaArray} depending on what value was used to set it. 534 */ 535 @Swap(JsonSchemaOrSchemaArraySwap.class) 536 public Object getItems() { 537 if (itemsSchema != null) 538 return itemsSchema; 539 return itemsSchemaArray; 540 } 541 542 /** 543 * Bean property getter: <property>items</property>. 544 * 545 * <p> 546 * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchema} value. 547 * 548 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}. 549 */ 550 public JsonSchema getItemsAsSchema() { 551 return itemsSchema; 552 } 553 554 /** 555 * Bean property getter: <property>items</property>. 556 * 557 * <p> 558 * Convenience method for returning the <property>items</property> property when it is a {@link JsonSchemaArray} value. 559 * 560 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}. 561 */ 562 public JsonSchemaArray getItemsAsSchemaArray() { 563 return itemsSchemaArray; 564 } 565 566 /** 567 * Used during parsing to convert the <property>items</property> property to the correct class type. 568 * 569 * <ul class='spaced-list'> 570 * <li> 571 * If parsing a JSON-array, converts to a {@link JsonSchemaArray}. 572 * <li> 573 * If parsing a JSON-object, converts to a {@link JsonSchema}. 574 * </ul> 575 * 576 * <p> 577 * Serialization method is a no-op. 578 */ 579 public static class JsonSchemaOrSchemaArraySwap extends ObjectSwap<Object,Object> { 580 581 @Override /* ObjectSwap */ 582 public Object swap(BeanSession session, Object o) throws SerializeException { 583 return o; 584 } 585 586 @Override /* ObjectSwap */ 587 public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException { 588 ClassMeta<?> cm = ( 589 o instanceof Collection 590 ? session.getClassMeta(JsonSchemaArray.class) 591 : session.getClassMeta(JsonSchema.class) 592 ); 593 return session.convertToType(o, cm); 594 } 595 } 596 597 /** 598 * Bean property setter: <property>items</property>. 599 * 600 * @param 601 * items The new value for the <property>items</property> property on this bean. 602 * This object must be of type {@link JsonSchema} or {@link JsonSchemaArray}. 603 * @return This object. 604 * @throws BeanRuntimeException If invalid object type passed in. 605 */ 606 public JsonSchema setItems(Object items) { 607 this.itemsSchema = null; 608 this.itemsSchemaArray = null; 609 if (items != null) { 610 if (items instanceof JsonSchema) { 611 this.itemsSchema = (JsonSchema)items; 612 setMasterOn(this.itemsSchema); 613 } else if (items instanceof JsonSchemaArray) { 614 this.itemsSchemaArray = (JsonSchemaArray)items; 615 setMasterOn(this.itemsSchemaArray); 616 } else { 617 throw new BeanRuntimeException(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in. Must be one of the following: JsonSchema, JsonSchemaArray", className(items)); 618 } 619 } 620 return this; 621 } 622 623 /** 624 * Bean property appender: <property>items</property>. 625 * 626 * @param items The list of items to append to the <property>items</property> property on this bean. 627 * @return This object. 628 */ 629 public JsonSchema addItems(JsonSchema...items) { 630 if (this.itemsSchemaArray == null) 631 this.itemsSchemaArray = new JsonSchemaArray(); 632 this.itemsSchemaArray.addAll(items); 633 setMasterOn(items); 634 return this; 635 } 636 637 /** 638 * Bean property getter: <property>multipleOf</property>. 639 * 640 * @return The value of the <property>multipleOf</property> property on this bean, or <jk>null</jk> if it is not set. 641 */ 642 public Number getMultipleOf() { 643 return multipleOf; 644 } 645 646 /** 647 * Bean property setter: <property>multipleOf</property>. 648 * 649 * @param multipleOf The new value for the <property>multipleOf</property> property on this bean. 650 * @return This object. 651 */ 652 public JsonSchema setMultipleOf(Number multipleOf) { 653 this.multipleOf = multipleOf; 654 return this; 655 } 656 657 /** 658 * Bean property getter: <property>maximum</property>. 659 * 660 * @return The value of the <property>maximum</property> property on this bean, or <jk>null</jk> if it is not set. 661 */ 662 public Number getMaximum() { 663 return maximum; 664 } 665 666 /** 667 * Bean property setter: <property>maximum</property>. 668 * 669 * @param maximum The new value for the <property>maximum</property> property on this bean. 670 * @return This object. 671 */ 672 public JsonSchema setMaximum(Number maximum) { 673 this.maximum = maximum; 674 return this; 675 } 676 677 /** 678 * Bean property getter: <property>exclusiveMaximum</property>. 679 * 680 * @return 681 * The value of the <property>exclusiveMaximum</property> property on this bean, or <jk>null</jk> if it is 682 * not set. 683 */ 684 public Boolean isExclusiveMaximum() { 685 return exclusiveMaximum; 686 } 687 688 /** 689 * Bean property setter: <property>exclusiveMaximum</property>. 690 * 691 * @param exclusiveMaximum The new value for the <property>exclusiveMaximum</property> property on this bean. 692 * @return This object. 693 */ 694 public JsonSchema setExclusiveMaximum(Boolean exclusiveMaximum) { 695 this.exclusiveMaximum = exclusiveMaximum; 696 return this; 697 } 698 699 /** 700 * Bean property getter: <property>minimum</property>. 701 * 702 * @return The value of the <property>minimum</property> property on this bean, or <jk>null</jk> if it is not set. 703 */ 704 public Number getMinimum() { 705 return minimum; 706 } 707 708 /** 709 * Bean property setter: <property>minimum</property>. 710 * 711 * @param minimum The new value for the <property>minimum</property> property on this bean. 712 * @return This object. 713 */ 714 public JsonSchema setMinimum(Number minimum) { 715 this.minimum = minimum; 716 return this; 717 } 718 719 /** 720 * Bean property getter: <property>exclusiveMinimum</property>. 721 * 722 * @return 723 * The value of the <property>exclusiveMinimum</property> property on this bean, or <jk>null</jk> if it is 724 * not set. 725 */ 726 public Boolean isExclusiveMinimum() { 727 return exclusiveMinimum; 728 } 729 730 /** 731 * Bean property setter: <property>exclusiveMinimum</property>. 732 * 733 * @param exclusiveMinimum The new value for the <property>exclusiveMinimum</property> property on this bean. 734 * @return This object. 735 */ 736 public JsonSchema setExclusiveMinimum(Boolean exclusiveMinimum) { 737 this.exclusiveMinimum = exclusiveMinimum; 738 return this; 739 } 740 741 /** 742 * Bean property getter: <property>maxLength</property>. 743 * 744 * @return The value of the <property>maxLength</property> property on this bean, or <jk>null</jk> if it is not set. 745 */ 746 public Integer getMaxLength() { 747 return maxLength; 748 } 749 750 /** 751 * Bean property setter: <property>maxLength</property>. 752 * 753 * @param maxLength The new value for the <property>maxLength</property> property on this bean. 754 * @return This object. 755 */ 756 public JsonSchema setMaxLength(Integer maxLength) { 757 this.maxLength = maxLength; 758 return this; 759 } 760 761 /** 762 * Bean property getter: <property>minLength</property>. 763 * 764 * @return The value of the <property>minLength</property> property on this bean, or <jk>null</jk> if it is not set. 765 */ 766 public Integer getMinLength() { 767 return minLength; 768 } 769 770 /** 771 * Bean property setter: <property>minLength</property>. 772 * 773 * @param minLength The new value for the <property>minLength</property> property on this bean. 774 * @return This object. 775 */ 776 public JsonSchema setMinLength(Integer minLength) { 777 this.minLength = minLength; 778 return this; 779 } 780 781 /** 782 * Bean property getter: <property>pattern</property>. 783 * 784 * @return The value of the <property>pattern</property> property on this bean, or <jk>null</jk> if it is not set. 785 */ 786 public String getPattern() { 787 return pattern; 788 } 789 790 /** 791 * Bean property setter: <property>pattern</property>. 792 * 793 * @param pattern The new value for the <property>pattern</property> property on this bean. 794 * @return This object. 795 */ 796 public JsonSchema setPattern(String pattern) { 797 this.pattern = pattern; 798 return this; 799 } 800 801 /** 802 * Bean property getter: <property>additionalItems</property>. 803 * 804 * @return 805 * The value of the <property>additionalItems</property> property on this bean, or <jk>null</jk> if it is 806 * not set. 807 * Can be either a {@link Boolean} or {@link JsonSchemaArray} depending on what value was used to set it. 808 */ 809 @Swap(BooleanOrSchemaArraySwap.class) 810 public Object getAdditionalItems() { 811 if (additionalItemsBoolean != null) 812 return additionalItemsBoolean; 813 return additionalItemsSchemaArray; 814 } 815 816 /** 817 * Bean property getter: <property>additionalItems</property>. 818 * 819 * <p> 820 * Convenience method for returning the <property>additionalItems</property> property when it is a {@link Boolean} 821 * value. 822 * 823 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchemaArray}. 824 */ 825 public Boolean getAdditionalItemsAsBoolean() { 826 return additionalItemsBoolean; 827 } 828 829 /** 830 * Bean property getter: <property>additionalItems</property>. 831 * 832 * <p> 833 * Convenience method for returning the <property>additionalItems</property> property when it is a 834 * {@link JsonSchemaArray} value. 835 * 836 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}. 837 */ 838 public List<JsonSchema> getAdditionalItemsAsSchemaArray() { 839 return additionalItemsSchemaArray; 840 } 841 842 /** 843 * Bean property setter: <property>additionalItems</property>. 844 * 845 * @param additionalItems 846 * The new value for the <property>additionalItems</property> property on this bean. 847 * This object must be of type {@link Boolean} or {@link JsonSchemaArray}. 848 * @return This object. 849 * @throws BeanRuntimeException If invalid object type passed in. 850 */ 851 public JsonSchema setAdditionalItems(Object additionalItems) { 852 this.additionalItemsBoolean = null; 853 this.additionalItemsSchemaArray = null; 854 if (additionalItems != null) { 855 if (additionalItems instanceof Boolean) 856 this.additionalItemsBoolean = (Boolean)additionalItems; 857 else if (additionalItems instanceof JsonSchemaArray) { 858 this.additionalItemsSchemaArray = (JsonSchemaArray)additionalItems; 859 setMasterOn(this.additionalItemsSchemaArray); 860 } else { 861 throw new BeanRuntimeException(JsonSchemaProperty.class, "Invalid attribute type ''{0}'' passed in. Must be one of the following: Boolean, JsonSchemaArray", className(additionalItems)); 862 } 863 } 864 return this; 865 } 866 867 /** 868 * Bean property appender: <property>additionalItems</property>. 869 * 870 * @param additionalItems 871 * The list of items to append to the <property>additionalItems</property> property on this bean. 872 * @return This object. 873 */ 874 public JsonSchema addAdditionalItems(JsonSchema...additionalItems) { 875 if (this.additionalItemsSchemaArray == null) 876 this.additionalItemsSchemaArray = new JsonSchemaArray(); 877 this.additionalItemsSchemaArray.addAll(additionalItems); 878 setMasterOn(additionalItems); 879 return this; 880 } 881 882 /** 883 * Used during parsing to convert the <property>additionalItems</property> property to the correct class type. 884 * 885 * <ul class='spaced-list'> 886 * <li> 887 * If parsing a JSON-array, converts to a {@link JsonSchemaArray}. 888 * <li> 889 * If parsing a JSON-boolean, converts to a {@link Boolean}. 890 * </ul> 891 * 892 * <p> 893 * Serialization method is a no-op. 894 */ 895 public static class BooleanOrSchemaArraySwap extends ObjectSwap<Object,Object> { 896 897 @Override /* ObjectSwap */ 898 public Object swap(BeanSession session, Object o) throws SerializeException { 899 return o; 900 } 901 902 @Override /* ObjectSwap */ 903 public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException { 904 ClassMeta<?> cm = ( 905 o instanceof Collection 906 ? session.getClassMeta(JsonSchemaArray.class) 907 : session.getClassMeta(Boolean.class) 908 ); 909 return session.convertToType(o, cm); 910 } 911 } 912 913 /** 914 * Bean property getter: <property>maxItems</property>. 915 * 916 * @return The value of the <property>maxItems</property> property on this bean, or <jk>null</jk> if it is not set. 917 */ 918 public Integer getMaxItems() { 919 return maxItems; 920 } 921 922 /** 923 * Bean property setter: <property>maxItems</property>. 924 * 925 * @param maxItems The new value for the <property>maxItems</property> property on this bean. 926 * @return This object. 927 */ 928 public JsonSchema setMaxItems(Integer maxItems) { 929 this.maxItems = maxItems; 930 return this; 931 } 932 933 /** 934 * Bean property getter: <property>minItems</property>. 935 * 936 * @return The value of the <property>minItems</property> property on this bean, or <jk>null</jk> if it is not set. 937 */ 938 public Integer getMinItems() { 939 return minItems; 940 } 941 942 /** 943 * Bean property setter: <property>minItems</property>. 944 * 945 * @param minItems The new value for the <property>minItems</property> property on this bean. 946 * @return This object. 947 */ 948 public JsonSchema setMinItems(Integer minItems) { 949 this.minItems = minItems; 950 return this; 951 } 952 953 /** 954 * Bean property getter: <property>uniqueItems</property>. 955 * 956 * @return 957 * The value of the <property>uniqueItems</property> property on this bean, or <jk>null</jk> if it is not set. 958 */ 959 public Boolean getUniqueItems() { 960 return uniqueItems; 961 } 962 963 /** 964 * Bean property setter: <property>uniqueItems</property>. 965 * 966 * @param uniqueItems The new value for the <property>uniqueItems</property> property on this bean. 967 * @return This object. 968 */ 969 public JsonSchema setUniqueItems(Boolean uniqueItems) { 970 this.uniqueItems = uniqueItems; 971 return this; 972 } 973 974 /** 975 * Bean property getter: <property>maxProperties</property>. 976 * 977 * @return 978 * The value of the <property>maxProperties</property> property on this bean, or <jk>null</jk> if it is not set. 979 */ 980 public Integer getMaxProperties() { 981 return maxProperties; 982 } 983 984 /** 985 * Bean property setter: <property>maxProperties</property>. 986 * 987 * @param maxProperties The new value for the <property>maxProperties</property> property on this bean. 988 * @return This object. 989 */ 990 public JsonSchema setMaxProperties(Integer maxProperties) { 991 this.maxProperties = maxProperties; 992 return this; 993 } 994 995 /** 996 * Bean property getter: <property>minProperties</property>. 997 * 998 * @return 999 * The value of the <property>minProperties</property> property on this bean, or <jk>null</jk> if it is not set. 1000 */ 1001 public Integer getMinProperties() { 1002 return minProperties; 1003 } 1004 1005 /** 1006 * Bean property setter: <property>minProperties</property>. 1007 * 1008 * @param minProperties The new value for the <property>minProperties</property> property on this bean. 1009 * @return This object. 1010 */ 1011 public JsonSchema setMinProperties(Integer minProperties) { 1012 this.minProperties = minProperties; 1013 return this; 1014 } 1015 1016 /** 1017 * Bean property getter: <property>required</property>. 1018 * 1019 * @return The value of the <property>required</property> property on this bean, or <jk>null</jk> if it is not set. 1020 */ 1021 public List<String> getRequired() { 1022 return required; 1023 } 1024 1025 /** 1026 * Bean property setter: <property>required</property>. 1027 * 1028 * @param required The new value for the <property>required</property> property on this bean. 1029 * @return This object. 1030 */ 1031 public JsonSchema setRequired(List<String> required) { 1032 this.required = required; 1033 return this; 1034 } 1035 1036 /** 1037 * Bean property appender: <property>required</property>. 1038 * 1039 * @param required The list of items to append to the <property>required</property> property on this bean. 1040 * @return This object. 1041 */ 1042 public JsonSchema addRequired(List<String> required) { 1043 if (this.required == null) 1044 this.required = new LinkedList<>(); 1045 required.forEach(x -> this.required.add(x)); 1046 return this; 1047 } 1048 1049 /** 1050 * Bean property appender: <property>required</property>. 1051 * 1052 * @param required The list of items to append to the <property>required</property> property on this bean. 1053 * @return This object. 1054 */ 1055 public JsonSchema addRequired(String...required) { 1056 if (this.required == null) 1057 this.required = new LinkedList<>(); 1058 for (String r : required) 1059 this.required.add(r); 1060 return this; 1061 } 1062 1063 /** 1064 * Bean property appender: <property>required</property>. 1065 * 1066 * @param properties The list of items to append to the <property>required</property> property on this bean. 1067 * @return This object. 1068 */ 1069 public JsonSchema addRequired(JsonSchemaProperty...properties) { 1070 if (this.required == null) 1071 this.required = new LinkedList<>(); 1072 for (JsonSchemaProperty p : properties) 1073 this.required.add(p.getName()); 1074 return this; 1075 } 1076 1077 /** 1078 * Bean property getter: <property>additionalProperties</property>. 1079 * 1080 * @return 1081 * The value of the <property>additionalProperties</property> property on this bean, or <jk>null</jk> if it 1082 * is not set. 1083 * Can be either a {@link Boolean} or {@link JsonSchemaArray} depending on what value was used to set it. 1084 */ 1085 @Swap(BooleanOrSchemaSwap.class) 1086 public Object getAdditionalProperties() { 1087 if (additionalPropertiesBoolean != null) 1088 return additionalItemsBoolean; 1089 return additionalPropertiesSchema; 1090 } 1091 1092 /** 1093 * Bean property getter: <property>additionalProperties</property>. 1094 * 1095 * <p> 1096 * Convenience method for returning the <property>additionalProperties</property> property when it is a 1097 * {@link Boolean} value. 1098 * 1099 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link JsonSchema}. 1100 */ 1101 public Boolean getAdditionalPropertiesAsBoolean() { 1102 return additionalPropertiesBoolean; 1103 } 1104 1105 /** 1106 * Bean property getter: <property>additionalProperties</property>. 1107 * 1108 * <p> 1109 * Convenience method for returning the <property>additionalProperties</property> property when it is a 1110 * {@link JsonSchema} value. 1111 * 1112 * @return The currently set value, or <jk>null</jk> if the property is not set, or is set as a {@link Boolean}. 1113 */ 1114 public JsonSchema getAdditionalPropertiesAsSchema() { 1115 return additionalPropertiesSchema; 1116 } 1117 1118 /** 1119 * Bean property setter: <property>additionalProperties</property>. 1120 * 1121 * @param additionalProperties 1122 * The new value for the <property>additionalProperties</property> property on this bean. 1123 * This object must be of type {@link Boolean} or {@link JsonSchema}. 1124 * @return This object. 1125 * @throws BeanRuntimeException If invalid object type passed in. 1126 */ 1127 @Beanp(dictionary={JsonSchema.class}) 1128 public JsonSchema setAdditionalProperties(Object additionalProperties) { 1129 this.additionalPropertiesBoolean = null; 1130 this.additionalPropertiesSchema = null; 1131 if (additionalProperties != null) { 1132 if (additionalProperties instanceof Boolean) 1133 this.additionalPropertiesBoolean = (Boolean)additionalProperties; 1134 else if (additionalProperties instanceof JsonSchema) { 1135 this.additionalPropertiesSchema = (JsonSchema)additionalProperties; 1136 setMasterOn(this.additionalPropertiesSchema); 1137 } else 1138 throw new BeanRuntimeException(JsonSchemaProperty.class, 1139 "Invalid attribute type ''{0}'' passed in. Must be one of the following: Boolean, JsonSchema", 1140 className(additionalProperties)); 1141 } 1142 return this; 1143 } 1144 1145 /** 1146 * Used during parsing to convert the <property>additionalProperties</property> property to the correct class type. 1147 * 1148 * <ul class='spaced-list'> 1149 * <li> 1150 * If parsing a JSON-object, converts to a {@link JsonSchema}. 1151 * <li> 1152 * If parsing a JSON-boolean, converts to a {@link Boolean}. 1153 * </ul> 1154 * 1155 * <p> 1156 * Serialization method is a no-op. 1157 */ 1158 public static class BooleanOrSchemaSwap extends ObjectSwap<Object,Object> { 1159 1160 @Override /* ObjectSwap */ 1161 public Object swap(BeanSession session, Object o) throws SerializeException { 1162 return o; 1163 } 1164 1165 @Override /* ObjectSwap */ 1166 public Object unswap(BeanSession session, Object o, ClassMeta<?> hint) throws ParseException { 1167 ClassMeta<?> cm = ( 1168 o instanceof Boolean 1169 ? session.getClassMeta(Boolean.class) 1170 : session.getClassMeta(JsonSchema.class) 1171 ); 1172 return session.convertToType(o, cm); 1173 } 1174 } 1175 1176 /** 1177 * Bean property getter: <property>enum</property>. 1178 * 1179 * @return The value of the <property>enum</property> property on this bean, or <jk>null</jk> if it is not set. 1180 */ 1181 public List<String> getEnum() { 1182 return _enum; 1183 } 1184 1185 /** 1186 * Bean property setter: <property>enum</property>. 1187 * 1188 * @param _enum The new value for the <property>enum</property> property on this bean. 1189 * @return This object. 1190 */ 1191 public JsonSchema setEnum(List<String> _enum) { 1192 this._enum = _enum; 1193 return this; 1194 } 1195 1196 /** 1197 * Bean property appender: <property>enum</property>. 1198 * 1199 * @param _enum The list of items to append to the <property>enum</property> property on this bean. 1200 * @return This object. 1201 */ 1202 public JsonSchema addEnum(String..._enum) { 1203 if (this._enum == null) 1204 this._enum = new LinkedList<>(); 1205 for (String e : _enum) 1206 this._enum.add(e); 1207 return this; 1208 } 1209 1210 /** 1211 * Bean property getter: <property>allOf</property>. 1212 * 1213 * @return The value of the <property>allOf</property> property on this bean, or <jk>null</jk> if it is not set. 1214 */ 1215 public List<JsonSchema> getAllOf() { 1216 return allOf; 1217 } 1218 1219 /** 1220 * Bean property setter: <property>allOf</property>. 1221 * 1222 * @param allOf The new value for the <property>allOf</property> property on this bean. 1223 * @return This object. 1224 */ 1225 public JsonSchema setAllOf(List<JsonSchema> allOf) { 1226 this.allOf = allOf; 1227 setMasterOn(allOf); 1228 return this; 1229 } 1230 1231 /** 1232 * Bean property appender: <property>allOf</property>. 1233 * 1234 * @param allOf The list of items to append to the <property>allOf</property> property on this bean. 1235 * @return This object. 1236 */ 1237 public JsonSchema addAllOf(JsonSchema...allOf) { 1238 setMasterOn(allOf); 1239 this.allOf = addAll(this.allOf, allOf); 1240 return this; 1241 } 1242 1243 /** 1244 * Bean property getter: <property>anyOf</property>. 1245 * 1246 * @return The value of the <property>anyOf</property> property on this bean, or <jk>null</jk> if it is not set. 1247 */ 1248 public List<JsonSchema> getAnyOf() { 1249 return anyOf; 1250 } 1251 1252 /** 1253 * Bean property setter: <property>anyOf</property>. 1254 * 1255 * @param anyOf The new value of the <property>anyOf</property> property on this bean. 1256 * @return This object. 1257 */ 1258 public JsonSchema setAnyOf(List<JsonSchema> anyOf) { 1259 this.anyOf = anyOf; 1260 setMasterOn(anyOf); 1261 return this; 1262 } 1263 1264 /** 1265 * Bean property appender: <property>anyOf</property>. 1266 * 1267 * @param anyOf The list of items to append to the <property>anyOf</property> property on this bean. 1268 * @return This object. 1269 */ 1270 public JsonSchema addAnyOf(JsonSchema...anyOf) { 1271 if (this.anyOf == null) 1272 this.anyOf = new LinkedList<>(); 1273 setMasterOn(anyOf); 1274 for (JsonSchema s : anyOf) 1275 this.anyOf.add(s); 1276 return this; 1277 } 1278 1279 /** 1280 * Bean property getter: <property>oneOf</property>. 1281 * 1282 * @return The value of the <property>oneOf</property> property on this bean, or <jk>null</jk> if it is not set. 1283 */ 1284 public List<JsonSchema> getOneOf() { 1285 return oneOf; 1286 } 1287 1288 /** 1289 * Bean property setter: <property>oneOf</property>. 1290 * 1291 * @param oneOf The new value for the <property>oneOf</property> property on this bean. 1292 * @return This object. 1293 */ 1294 public JsonSchema setOneOf(List<JsonSchema> oneOf) { 1295 this.oneOf = oneOf; 1296 setMasterOn(oneOf); 1297 return this; 1298 } 1299 1300 /** 1301 * Bean property appender: <property>oneOf</property>. 1302 * 1303 * @param oneOf The list of items to append to the <property>oneOf</property> property on this bean. 1304 * @return This object. 1305 */ 1306 public JsonSchema addOneOf(JsonSchema...oneOf) { 1307 if (this.oneOf == null) 1308 this.oneOf = new LinkedList<>(); 1309 setMasterOn(oneOf); 1310 for (JsonSchema s : oneOf) 1311 this.oneOf.add(s); 1312 return this; 1313 } 1314 1315 /** 1316 * Bean property getter: <property>not</property>. 1317 * 1318 * @return The value of the <property>not</property> property on this bean, or <jk>null</jk> if it is not set. 1319 */ 1320 public JsonSchema getNot() { 1321 return not; 1322 } 1323 1324 /** 1325 * Bean property setter: <property>not</property>. 1326 * 1327 * @param not The new value for the <property>not</property> property on this bean. 1328 * @return This object. 1329 */ 1330 public JsonSchema setNot(JsonSchema not) { 1331 this.not = not; 1332 setMasterOn(not); 1333 return this; 1334 } 1335 1336 /** 1337 * Bean property getter: <property>$ref</property>. 1338 * 1339 * @return The value of the <property>$ref</property> property on this bean, or <jk>null</jk> if it is not set. 1340 */ 1341 @Beanp("$ref") 1342 public URI getRef() { 1343 return ref; 1344 } 1345 1346 /** 1347 * Bean property setter: <property>$ref</property>. 1348 * 1349 * <p> 1350 * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}. 1351 * Strings must be valid URIs. 1352 * 1353 * <p> 1354 * URIs defined by {@link UriResolver} can be used for values. 1355 * 1356 * @param ref The new value for the <property>$ref</property> property on this bean. 1357 * @return This object. 1358 */ 1359 @Beanp("$ref") 1360 public JsonSchema setRef(Object ref) { 1361 this.ref = toURI(ref); 1362 return this; 1363 } 1364 1365 private void setMasterOn(JsonSchema s) { 1366 if (s != null) 1367 s.setMaster(this); 1368 } 1369 1370 private void setMasterOn(JsonSchema[] ss) { 1371 if (ss != null) 1372 for (JsonSchema s : ss) 1373 setMasterOn(s); 1374 } 1375 1376 private void setMasterOn(Collection<JsonSchema> ss) { 1377 if (ss != null) 1378 ss.forEach(x -> setMasterOn(x)); 1379 } 1380 1381 private void setMasterOn(JsonSchemaArray ss) { 1382 if (ss != null) 1383 ss.forEach(x -> setMasterOn(x)); 1384 } 1385 1386 /** 1387 * Sets the master schema for this schema and all child schema objects. 1388 * 1389 * <p> 1390 * All child elements in a schema should point to a single "master" schema in order to locate registered JsonSchemaMap 1391 * objects for resolving external schemas. 1392 * 1393 * @param master The master schema to associate on this and all children. Can be <jk>null</jk>. 1394 */ 1395 protected void setMaster(JsonSchema master) { 1396 this.master = master; 1397 if (definitions != null) 1398 definitions.values().forEach(x -> x.setMaster(master)); 1399 if (properties != null) 1400 properties.values().forEach(x -> x.setMaster(master)); 1401 if (patternProperties != null) 1402 patternProperties.values().forEach(x -> x.setMaster(master)); 1403 if (dependencies != null) 1404 dependencies.values().forEach(x -> x.setMaster(master)); 1405 if (itemsSchema != null) 1406 itemsSchema.setMaster(master); 1407 if (itemsSchemaArray != null) 1408 itemsSchemaArray.forEach(x -> x.setMaster(master)); 1409 if (additionalItemsSchemaArray != null) 1410 additionalItemsSchemaArray.forEach(x -> x.setMaster(master)); 1411 if (additionalPropertiesSchema != null) 1412 additionalPropertiesSchema.setMaster(master); 1413 if (allOf != null) 1414 allOf.forEach(x -> x.setMaster(master)); 1415 if (anyOf != null) 1416 anyOf.forEach(x -> x.setMaster(master)); 1417 if (oneOf != null) 1418 oneOf.forEach(x -> x.setMaster(master)); 1419 if (not != null) 1420 not.setMaster(master); 1421 } 1422 1423 /** 1424 * Resolve schema if reference. 1425 * 1426 * <p> 1427 * If this schema is a reference to another schema (has its <property>$ref</property> property set), this 1428 * method will retrieve the referenced schema from the schema map registered with this schema. 1429 * 1430 * <p> 1431 * If this schema is not a reference, or no schema map is registered with this schema, this method is a no-op and 1432 * simply returns this object. 1433 * 1434 * @return The referenced schema, or <jk>null</jk>. 1435 */ 1436 public JsonSchema resolve() { 1437 if (ref == null || master.schemaMap == null) 1438 return this; 1439 return master.schemaMap.get(ref); 1440 } 1441 1442 /** 1443 * Associates a schema map with this schema for resolving other schemas identified through <property>$ref</property> 1444 * properties. 1445 * 1446 * @param schemaMap The schema map to associate with this schema. Can be <jk>null</jk>. 1447 * @return This object. 1448 */ 1449 public JsonSchema setSchemaMap(JsonSchemaMap schemaMap) { 1450 this.schemaMap = schemaMap; 1451 return this; 1452 } 1453 1454 @Override /* Object */ 1455 public String toString() { 1456 return JsonSerializer.DEFAULT.toString(this); 1457 } 1458}