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.jsonschema; 014 015import static org.apache.juneau.collections.JsonMap.*; 016import static org.apache.juneau.common.internal.StringUtils.*; 017import static org.apache.juneau.common.internal.ThrowableUtils.*; 018import static org.apache.juneau.internal.CollectionUtils.*; 019import static java.util.Collections.*; 020 021import java.lang.annotation.*; 022import java.lang.reflect.*; 023import java.util.*; 024import java.util.concurrent.*; 025import java.util.regex.*; 026 027import org.apache.juneau.*; 028import org.apache.juneau.annotation.*; 029import org.apache.juneau.collections.*; 030import org.apache.juneau.internal.*; 031import org.apache.juneau.json.*; 032import org.apache.juneau.utils.*; 033 034/** 035 * Generates JSON-schema metadata about POJOs. 036 * 037 * <h5 class='section'>Notes:</h5><ul> 038 * <li class='note'>This class is thread safe and reusable. 039 * </ul> 040 * 041 * <p> 042 * <h5 class='section'>See Also:</h5><ul> 043 * <li class='link'><a class="doclink" href="../../../../index.html#jm.JsonSchemaDetails">JSON-Schema Support</a> 044 * </ul> 045 */ 046public class JsonSchemaGenerator extends BeanTraverseContext implements JsonSchemaMetaProvider { 047 048 //------------------------------------------------------------------------------------------------------------------- 049 // Static 050 //------------------------------------------------------------------------------------------------------------------- 051 052 /** Default serializer, all default settings.*/ 053 public static final JsonSchemaGenerator DEFAULT = new JsonSchemaGenerator(create()); 054 055 /** 056 * Creates a new builder for this object. 057 * 058 * @return A new builder. 059 */ 060 public static Builder create() { 061 return new Builder(); 062 } 063 064 //------------------------------------------------------------------------------------------------------------------- 065 // Builder 066 //------------------------------------------------------------------------------------------------------------------- 067 068 /** 069 * Builder class. 070 */ 071 @FluentSetters 072 public static class Builder extends BeanTraverseContext.Builder { 073 074 private static final Cache<HashKey,JsonSchemaGenerator> CACHE = Cache.of(HashKey.class, JsonSchemaGenerator.class).build(); 075 076 final JsonSerializer.Builder jsonSerializerBuilder; 077 final JsonParser.Builder jsonParserBuilder; 078 079 SortedSet<TypeCategory> addDescriptionsTo, addExamplesTo; 080 boolean allowNestedDescriptions, allowNestedExamples, useBeanDefs; 081 Class<? extends BeanDefMapper> beanDefMapper; 082 SortedSet<String> ignoreTypes; 083 084 /** 085 * Constructor, default settings. 086 */ 087 protected Builder() { 088 super(); 089 BeanContext.Builder bc = beanContext(); 090 jsonSerializerBuilder = JsonSerializer.create().beanContext(bc); 091 jsonParserBuilder = JsonParser.create().beanContext(bc); 092 registerBuilders(jsonSerializerBuilder, jsonParserBuilder); 093 addDescriptionsTo = null; 094 addExamplesTo = null; 095 allowNestedDescriptions = env("JsonSchemaGenerator.allowNestedDescriptions", false); 096 allowNestedExamples = env("JsonSchemaGenerator.allowNestedExamples", false); 097 useBeanDefs = env("JsonSchemaGenerator.useBeanDefs", false); 098 beanDefMapper = BasicBeanDefMapper.class; 099 ignoreTypes = null; 100 } 101 102 /** 103 * Copy constructor. 104 * 105 * @param copyFrom The bean to copy from. 106 */ 107 protected Builder(JsonSchemaGenerator copyFrom) { 108 super(copyFrom); 109 BeanContext.Builder bc = beanContext(); 110 jsonSerializerBuilder = copyFrom.jsonSerializer.copy().beanContext(bc); 111 jsonParserBuilder = copyFrom.jsonParser.copy().beanContext(bc); 112 registerBuilders(jsonSerializerBuilder, jsonParserBuilder); 113 addDescriptionsTo = copyFrom.addDescriptionsTo.isEmpty() ? null : new TreeSet<>(copyFrom.addDescriptionsTo); 114 addExamplesTo = copyFrom.addExamplesTo.isEmpty() ? null : new TreeSet<>(copyFrom.addExamplesTo); 115 allowNestedDescriptions = copyFrom.allowNestedDescriptions; 116 allowNestedExamples = copyFrom.allowNestedExamples; 117 useBeanDefs = copyFrom.useBeanDefs; 118 beanDefMapper = copyFrom.beanDefMapper; 119 ignoreTypes = copyFrom.ignoreTypes.isEmpty() ? null : new TreeSet<>(copyFrom.ignoreTypes); 120 } 121 122 /** 123 * Copy constructor. 124 * 125 * @param copyFrom The builder to copy from. 126 */ 127 protected Builder(Builder copyFrom) { 128 super(copyFrom); 129 BeanContext.Builder bc = beanContext(); 130 jsonSerializerBuilder = copyFrom.jsonSerializerBuilder.copy().beanContext(bc); 131 jsonParserBuilder = copyFrom.jsonParserBuilder.copy().beanContext(bc); 132 registerBuilders(jsonSerializerBuilder, jsonParserBuilder); 133 addDescriptionsTo = copyFrom.addDescriptionsTo == null ? null : new TreeSet<>(copyFrom.addDescriptionsTo); 134 addExamplesTo = copyFrom.addExamplesTo == null ? null : new TreeSet<>(copyFrom.addExamplesTo); 135 allowNestedDescriptions = copyFrom.allowNestedDescriptions; 136 allowNestedExamples = copyFrom.allowNestedExamples; 137 useBeanDefs = copyFrom.useBeanDefs; 138 beanDefMapper = copyFrom.beanDefMapper; 139 ignoreTypes = copyFrom.ignoreTypes == null ? null : new TreeSet<>(copyFrom.ignoreTypes); 140 } 141 142 @Override /* Context.Builder */ 143 public Builder copy() { 144 return new Builder(this); 145 } 146 147 @Override /* Context.Builder */ 148 public JsonSchemaGenerator build() { 149 return cache(CACHE).build(JsonSchemaGenerator.class); 150 } 151 152 @Override /* Context.Builder */ 153 public HashKey hashKey() { 154 return HashKey.of( 155 super.hashKey(), 156 jsonSerializerBuilder.hashKey(), 157 jsonParserBuilder.hashKey(), 158 addDescriptionsTo, 159 addExamplesTo, 160 allowNestedDescriptions, 161 allowNestedExamples, 162 useBeanDefs, 163 beanDefMapper, 164 ignoreTypes 165 ); 166 } 167 168 //----------------------------------------------------------------------------------------------------------------- 169 // Properties 170 //----------------------------------------------------------------------------------------------------------------- 171 172 /** 173 * Add descriptions. 174 * 175 * <p> 176 * Identifies which categories of types that descriptions should be automatically added to generated schemas. 177 * The description is the result of calling {@link ClassMeta#getFullName()}. 178 * The format is a comma-delimited list of any of the following values: 179 * 180 * <ul class='javatree'> 181 * <li class='jf'>{@link TypeCategory#BEAN BEAN} 182 * <li class='jf'>{@link TypeCategory#COLLECTION COLLECTION} 183 * <li class='jf'>{@link TypeCategory#ARRAY ARRAY} 184 * <li class='jf'>{@link TypeCategory#MAP MAP} 185 * <li class='jf'>{@link TypeCategory#STRING STRING} 186 * <li class='jf'>{@link TypeCategory#NUMBER NUMBER} 187 * <li class='jf'>{@link TypeCategory#BOOLEAN BOOLEAN} 188 * <li class='jf'>{@link TypeCategory#ANY ANY} 189 * <li class='jf'>{@link TypeCategory#OTHER OTHER} 190 * </ul> 191 * 192 * @param values 193 * The values to add to this setting. 194 * <br>The default is an empty string. 195 * @return This object. 196 */ 197 @FluentSetter 198 public Builder addDescriptionsTo(TypeCategory...values) { 199 addDescriptionsTo = addAll(addDescriptionsTo, values); 200 return this; 201 } 202 203 /** 204 * Add examples. 205 * 206 * <p> 207 * Identifies which categories of types that examples should be automatically added to generated schemas. 208 * <p> 209 * The examples come from calling {@link ClassMeta#getExample(BeanSession,JsonParserSession)} which in turn gets examples 210 * from the following: 211 * <ul class='javatree'> 212 * <li class='ja'>{@link Example} 213 * <li class='ja'>{@link Marshalled#example() Marshalled(example)} 214 * </ul> 215 * 216 * <p> 217 * The format is a comma-delimited list of any of the following values: 218 * 219 * <ul class='javatree'> 220 * <li class='jf'>{@link TypeCategory#BEAN BEAN} 221 * <li class='jf'>{@link TypeCategory#COLLECTION COLLECTION} 222 * <li class='jf'>{@link TypeCategory#ARRAY ARRAY} 223 * <li class='jf'>{@link TypeCategory#MAP MAP} 224 * <li class='jf'>{@link TypeCategory#STRING STRING} 225 * <li class='jf'>{@link TypeCategory#NUMBER NUMBER} 226 * <li class='jf'>{@link TypeCategory#BOOLEAN BOOLEAN} 227 * <li class='jf'>{@link TypeCategory#ANY ANY} 228 * <li class='jf'>{@link TypeCategory#OTHER OTHER} 229 * </ul> 230 * 231 * @param values 232 * The values to add to this setting. 233 * <br>The default is an empty string. 234 * @return This object. 235 */ 236 @FluentSetter 237 public Builder addExamplesTo(TypeCategory...values) { 238 addExamplesTo = addAll(addExamplesTo, values); 239 return this; 240 } 241 242 /** 243 * Allow nested descriptions. 244 * 245 * <p> 246 * Identifies whether nested descriptions are allowed in schema definitions. 247 * 248 * @return This object. 249 */ 250 @FluentSetter 251 public Builder allowNestedDescriptions() { 252 return allowNestedDescriptions(true); 253 } 254 255 /** 256 * Same as {@link #allowNestedDescriptions()} but allows you to explicitly specify the value. 257 * 258 * @param value The value for this setting. 259 * @return This object. 260 */ 261 @FluentSetter 262 public Builder allowNestedDescriptions(boolean value) { 263 allowNestedDescriptions = value; 264 return this; 265 } 266 267 /** 268 * Allow nested examples. 269 * 270 * <p> 271 * Identifies whether nested examples are allowed in schema definitions. 272 * 273 * @return This object. 274 */ 275 @FluentSetter 276 public Builder allowNestedExamples() { 277 return allowNestedExamples(true); 278 } 279 280 /** 281 * Same as {@link #allowNestedExamples()} but allows you to explicitly specify the value. 282 * 283 * @param value The value for this setting. 284 * @return This object. 285 */ 286 @FluentSetter 287 public Builder allowNestedExamples(boolean value) { 288 allowNestedExamples = value; 289 return this; 290 } 291 292 /** 293 * Schema definition mapper. 294 * 295 * <p> 296 * Interface to use for converting Bean classes to definition IDs and URIs. 297 * <p> 298 * Used primarily for defining common definition sections for beans in Swagger JSON. 299 * <p> 300 * This setting is ignored if {@link JsonSchemaGenerator.Builder#useBeanDefs()} is not enabled. 301 * 302 * @param value 303 * The new value for this setting. 304 * <br>The default is {@link org.apache.juneau.jsonschema.BasicBeanDefMapper}. 305 * @return This object. 306 */ 307 @FluentSetter 308 public Builder beanDefMapper(Class<? extends BeanDefMapper> value) { 309 beanDefMapper = value; 310 return this; 311 } 312 313 /** 314 * Ignore types from schema definitions. 315 * 316 * <h5 class='section'>Description:</h5> 317 * <p> 318 * Defines class name patterns that should be ignored when generating schema definitions in the generated 319 * Swagger documentation. 320 * 321 * <h5 class='section'>Example:</h5> 322 * <p class='bjava'> 323 * <jc>// Don't generate schema for any prototype packages or the class named 'Swagger'.</jc> 324 * <ja>@JsonSchemaConfig</ja>( 325 * ignoreTypes=<js>"Swagger,*.proto.*"</js> 326 * ) 327 * <jk>public class</jk> MyResource {...} 328 * </p> 329 * 330 * @param values 331 * The values to add. 332 * @return This object. 333 */ 334 @FluentSetter 335 public Builder ignoreTypes(String...values) { 336 ignoreTypes = addAll(ignoreTypes, values); 337 return this; 338 } 339 340 /** 341 * Use bean definitions. 342 * 343 * <p> 344 * When enabled, schemas on beans will be serialized as the following: 345 * <p class='bjson'> 346 * { 347 * type: <js>'object'</js>, 348 * <js>'$ref'</js>: <js>'#/definitions/TypeId'</js> 349 * } 350 * </p> 351 * 352 * <p> 353 * The definitions can then be retrieved from the session using {@link JsonSchemaGeneratorSession#getBeanDefs()}. 354 * <p> 355 * Definitions can also be added programmatically using {@link JsonSchemaGeneratorSession#addBeanDef(String, JsonMap)}. 356 * 357 * @return This object. 358 */ 359 @FluentSetter 360 public Builder useBeanDefs() { 361 return useBeanDefs(true); 362 } 363 364 /** 365 * Same as {@link #useBeanDefs()} but allows you to explicitly specify the value. 366 * 367 * @param value The value for this setting. 368 * @return This object. 369 */ 370 @FluentSetter 371 public Builder useBeanDefs(boolean value) { 372 useBeanDefs = value; 373 return this; 374 } 375 376 /** 377 * Gives access to the inner JSON serializer builder if you want to modify the serializer settings. 378 * 379 * @return The JSON serializer builder. 380 */ 381 public JsonSerializer.Builder getJsonSerializerBuilder() { 382 return jsonSerializerBuilder; 383 } 384 385 /** 386 * Gives access to the inner JSON parser builder if you want to modify the parser settings. 387 * 388 * @return The JSON serializer builder. 389 */ 390 public JsonParser.Builder getJsonParserBuilder() { 391 return jsonParserBuilder; 392 } 393 394 // <FluentSetters> 395 396 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 397 public Builder annotations(Annotation...values) { 398 super.annotations(values); 399 return this; 400 } 401 402 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 403 public Builder apply(AnnotationWorkList work) { 404 super.apply(work); 405 return this; 406 } 407 408 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 409 public Builder applyAnnotations(java.lang.Class<?>...fromClasses) { 410 super.applyAnnotations(fromClasses); 411 return this; 412 } 413 414 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 415 public Builder applyAnnotations(Method...fromMethods) { 416 super.applyAnnotations(fromMethods); 417 return this; 418 } 419 420 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 421 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 422 super.cache(value); 423 return this; 424 } 425 426 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 427 public Builder debug() { 428 super.debug(); 429 return this; 430 } 431 432 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 433 public Builder debug(boolean value) { 434 super.debug(value); 435 return this; 436 } 437 438 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 439 public Builder impl(Context value) { 440 super.impl(value); 441 return this; 442 } 443 444 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 445 public Builder type(Class<? extends org.apache.juneau.Context> value) { 446 super.type(value); 447 return this; 448 } 449 450 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 451 public Builder beanClassVisibility(Visibility value) { 452 super.beanClassVisibility(value); 453 return this; 454 } 455 456 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 457 public Builder beanConstructorVisibility(Visibility value) { 458 super.beanConstructorVisibility(value); 459 return this; 460 } 461 462 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 463 public Builder beanContext(BeanContext value) { 464 super.beanContext(value); 465 return this; 466 } 467 468 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 469 public Builder beanContext(BeanContext.Builder value) { 470 super.beanContext(value); 471 return this; 472 } 473 474 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 475 public Builder beanDictionary(java.lang.Class<?>...values) { 476 super.beanDictionary(values); 477 return this; 478 } 479 480 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 481 public Builder beanFieldVisibility(Visibility value) { 482 super.beanFieldVisibility(value); 483 return this; 484 } 485 486 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 487 public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) { 488 super.beanInterceptor(on, value); 489 return this; 490 } 491 492 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 493 public Builder beanMapPutReturnsOldValue() { 494 super.beanMapPutReturnsOldValue(); 495 return this; 496 } 497 498 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 499 public Builder beanMethodVisibility(Visibility value) { 500 super.beanMethodVisibility(value); 501 return this; 502 } 503 504 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 505 public Builder beanProperties(Map<String,Object> values) { 506 super.beanProperties(values); 507 return this; 508 } 509 510 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 511 public Builder beanProperties(Class<?> beanClass, String properties) { 512 super.beanProperties(beanClass, properties); 513 return this; 514 } 515 516 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 517 public Builder beanProperties(String beanClassName, String properties) { 518 super.beanProperties(beanClassName, properties); 519 return this; 520 } 521 522 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 523 public Builder beanPropertiesExcludes(Map<String,Object> values) { 524 super.beanPropertiesExcludes(values); 525 return this; 526 } 527 528 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 529 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 530 super.beanPropertiesExcludes(beanClass, properties); 531 return this; 532 } 533 534 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 535 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 536 super.beanPropertiesExcludes(beanClassName, properties); 537 return this; 538 } 539 540 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 541 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 542 super.beanPropertiesReadOnly(values); 543 return this; 544 } 545 546 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 547 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 548 super.beanPropertiesReadOnly(beanClass, properties); 549 return this; 550 } 551 552 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 553 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 554 super.beanPropertiesReadOnly(beanClassName, properties); 555 return this; 556 } 557 558 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 559 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 560 super.beanPropertiesWriteOnly(values); 561 return this; 562 } 563 564 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 565 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 566 super.beanPropertiesWriteOnly(beanClass, properties); 567 return this; 568 } 569 570 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 571 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 572 super.beanPropertiesWriteOnly(beanClassName, properties); 573 return this; 574 } 575 576 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 577 public Builder beansRequireDefaultConstructor() { 578 super.beansRequireDefaultConstructor(); 579 return this; 580 } 581 582 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 583 public Builder beansRequireSerializable() { 584 super.beansRequireSerializable(); 585 return this; 586 } 587 588 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 589 public Builder beansRequireSettersForGetters() { 590 super.beansRequireSettersForGetters(); 591 return this; 592 } 593 594 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 595 public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) { 596 super.dictionaryOn(on, values); 597 return this; 598 } 599 600 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 601 public Builder disableBeansRequireSomeProperties() { 602 super.disableBeansRequireSomeProperties(); 603 return this; 604 } 605 606 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 607 public Builder disableIgnoreMissingSetters() { 608 super.disableIgnoreMissingSetters(); 609 return this; 610 } 611 612 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 613 public Builder disableIgnoreTransientFields() { 614 super.disableIgnoreTransientFields(); 615 return this; 616 } 617 618 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 619 public Builder disableIgnoreUnknownNullBeanProperties() { 620 super.disableIgnoreUnknownNullBeanProperties(); 621 return this; 622 } 623 624 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 625 public Builder disableInterfaceProxies() { 626 super.disableInterfaceProxies(); 627 return this; 628 } 629 630 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 631 public <T> Builder example(Class<T> pojoClass, T o) { 632 super.example(pojoClass, o); 633 return this; 634 } 635 636 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 637 public <T> Builder example(Class<T> pojoClass, String json) { 638 super.example(pojoClass, json); 639 return this; 640 } 641 642 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 643 public Builder findFluentSetters() { 644 super.findFluentSetters(); 645 return this; 646 } 647 648 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 649 public Builder findFluentSetters(Class<?> on) { 650 super.findFluentSetters(on); 651 return this; 652 } 653 654 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 655 public Builder ignoreInvocationExceptionsOnGetters() { 656 super.ignoreInvocationExceptionsOnGetters(); 657 return this; 658 } 659 660 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 661 public Builder ignoreInvocationExceptionsOnSetters() { 662 super.ignoreInvocationExceptionsOnSetters(); 663 return this; 664 } 665 666 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 667 public Builder ignoreUnknownBeanProperties() { 668 super.ignoreUnknownBeanProperties(); 669 return this; 670 } 671 672 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 673 public Builder ignoreUnknownEnumValues() { 674 super.ignoreUnknownEnumValues(); 675 return this; 676 } 677 678 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 679 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 680 super.implClass(interfaceClass, implClass); 681 return this; 682 } 683 684 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 685 public Builder implClasses(Map<Class<?>,Class<?>> values) { 686 super.implClasses(values); 687 return this; 688 } 689 690 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 691 public Builder interfaceClass(Class<?> on, Class<?> value) { 692 super.interfaceClass(on, value); 693 return this; 694 } 695 696 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 697 public Builder interfaces(java.lang.Class<?>...value) { 698 super.interfaces(value); 699 return this; 700 } 701 702 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 703 public Builder locale(Locale value) { 704 super.locale(value); 705 return this; 706 } 707 708 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 709 public Builder mediaType(MediaType value) { 710 super.mediaType(value); 711 return this; 712 } 713 714 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 715 public Builder notBeanClasses(java.lang.Class<?>...values) { 716 super.notBeanClasses(values); 717 return this; 718 } 719 720 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 721 public Builder notBeanPackages(String...values) { 722 super.notBeanPackages(values); 723 return this; 724 } 725 726 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 727 public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) { 728 super.propertyNamer(value); 729 return this; 730 } 731 732 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 733 public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) { 734 super.propertyNamer(on, value); 735 return this; 736 } 737 738 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 739 public Builder sortProperties() { 740 super.sortProperties(); 741 return this; 742 } 743 744 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 745 public Builder sortProperties(java.lang.Class<?>...on) { 746 super.sortProperties(on); 747 return this; 748 } 749 750 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 751 public Builder stopClass(Class<?> on, Class<?> value) { 752 super.stopClass(on, value); 753 return this; 754 } 755 756 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 757 public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 758 super.swap(normalClass, swappedClass, swapFunction); 759 return this; 760 } 761 762 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 763 public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 764 super.swap(normalClass, swappedClass, swapFunction, unswapFunction); 765 return this; 766 } 767 768 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 769 public Builder swaps(java.lang.Class<?>...values) { 770 super.swaps(values); 771 return this; 772 } 773 774 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 775 public Builder timeZone(TimeZone value) { 776 super.timeZone(value); 777 return this; 778 } 779 780 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 781 public Builder typeName(Class<?> on, String value) { 782 super.typeName(on, value); 783 return this; 784 } 785 786 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 787 public Builder typePropertyName(String value) { 788 super.typePropertyName(value); 789 return this; 790 } 791 792 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 793 public Builder typePropertyName(Class<?> on, String value) { 794 super.typePropertyName(on, value); 795 return this; 796 } 797 798 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 799 public Builder useEnumNames() { 800 super.useEnumNames(); 801 return this; 802 } 803 804 @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ 805 public Builder useJavaBeanIntrospector() { 806 super.useJavaBeanIntrospector(); 807 return this; 808 } 809 810 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 811 public Builder detectRecursions() { 812 super.detectRecursions(); 813 return this; 814 } 815 816 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 817 public Builder detectRecursions(boolean value) { 818 super.detectRecursions(value); 819 return this; 820 } 821 822 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 823 public Builder ignoreRecursions() { 824 super.ignoreRecursions(); 825 return this; 826 } 827 828 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 829 public Builder ignoreRecursions(boolean value) { 830 super.ignoreRecursions(value); 831 return this; 832 } 833 834 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 835 public Builder initialDepth(int value) { 836 super.initialDepth(value); 837 return this; 838 } 839 840 @Override /* GENERATED - org.apache.juneau.BeanTraverseContext.Builder */ 841 public Builder maxDepth(int value) { 842 super.maxDepth(value); 843 return this; 844 } 845 846 // </FluentSetters> 847 } 848 849 //------------------------------------------------------------------------------------------------------------------- 850 // Instance 851 //------------------------------------------------------------------------------------------------------------------- 852 853 final boolean useBeanDefs, allowNestedExamples, allowNestedDescriptions; 854 final Set<TypeCategory> addExamplesTo, addDescriptionsTo; 855 final Class<? extends BeanDefMapper> beanDefMapper; 856 final Set<String> ignoreTypes; 857 858 private final BeanDefMapper beanDefMapperBean; 859 final JsonSerializer jsonSerializer; 860 final JsonParser jsonParser; 861 private final Pattern[] ignoreTypePatterns; 862 private final Map<ClassMeta<?>,JsonSchemaClassMeta> jsonSchemaClassMetas = new ConcurrentHashMap<>(); 863 private final Map<BeanPropertyMeta,JsonSchemaBeanPropertyMeta> jsonSchemaBeanPropertyMetas = new ConcurrentHashMap<>(); 864 865 /** 866 * Constructor. 867 * 868 * @param builder The builder for this object. 869 */ 870 public JsonSchemaGenerator(Builder builder) { 871 super(builder.detectRecursions().ignoreRecursions()); 872 873 useBeanDefs = builder.useBeanDefs; 874 allowNestedExamples = builder.allowNestedExamples; 875 allowNestedDescriptions = builder.allowNestedDescriptions; 876 beanDefMapper = builder.beanDefMapper; 877 addExamplesTo = builder.addExamplesTo == null ? emptySet() : new TreeSet<>(builder.addExamplesTo); 878 addDescriptionsTo = builder.addDescriptionsTo == null ? emptySet() : new TreeSet<>(builder.addDescriptionsTo); 879 ignoreTypes = builder.ignoreTypes == null ? emptySet() : new TreeSet<>(builder.ignoreTypes); 880 881 Set<Pattern> ignoreTypePatterns = set(); 882 ignoreTypes.forEach(y -> split(y, x -> ignoreTypePatterns.add(Pattern.compile(x.replace(".", "\\.").replace("*", ".*"))))); 883 this.ignoreTypePatterns = ignoreTypePatterns.toArray(new Pattern[ignoreTypePatterns.size()]); 884 885 try { 886 beanDefMapperBean = beanDefMapper.getDeclaredConstructor().newInstance(); 887 } catch (Exception e) { 888 throw asRuntimeException(e); 889 } 890 891 jsonSerializer = builder.jsonSerializerBuilder.build(); 892 jsonParser = builder.jsonParserBuilder.beanContext(getBeanContext()).build(); 893 } 894 895 @Override /* Context */ 896 public Builder copy() { 897 return new Builder(this); 898 } 899 900 @Override /* Context */ 901 public JsonSchemaGeneratorSession.Builder createSession() { 902 return JsonSchemaGeneratorSession.create(this); 903 } 904 905 @Override /* Context */ 906 public JsonSchemaGeneratorSession getSession() { 907 return createSession().build(); 908 } 909 910 JsonSerializer getJsonSerializer() { 911 return jsonSerializer; 912 } 913 914 JsonParser getJsonParser() { 915 return jsonParser; 916 } 917 918 //----------------------------------------------------------------------------------------------------------------- 919 // Properties 920 //----------------------------------------------------------------------------------------------------------------- 921 922 /** 923 * Add descriptions to types. 924 * 925 * @see Builder#addDescriptionsTo(TypeCategory...) 926 * @return 927 * Set of categories of types that descriptions should be automatically added to generated schemas. 928 */ 929 protected final Set<TypeCategory> getAddDescriptionsTo() { 930 return addDescriptionsTo; 931 } 932 933 /** 934 * Add examples. 935 * 936 * @see Builder#addExamplesTo(TypeCategory...) 937 * @return 938 * Set of categories of types that examples should be automatically added to generated schemas. 939 */ 940 protected final Set<TypeCategory> getAddExamplesTo() { 941 return addExamplesTo; 942 } 943 944 /** 945 * Allow nested descriptions. 946 * 947 * @see Builder#allowNestedDescriptions() 948 * @return 949 * <jk>true</jk> if nested descriptions are allowed in schema definitions. 950 */ 951 protected final boolean isAllowNestedDescriptions() { 952 return allowNestedDescriptions; 953 } 954 955 /** 956 * Allow nested examples. 957 * 958 * @see Builder#allowNestedExamples() 959 * @return 960 * <jk>true</jk> if nested examples are allowed in schema definitions. 961 */ 962 protected final boolean isAllowNestedExamples() { 963 return allowNestedExamples; 964 } 965 966 /** 967 * Bean schema definition mapper. 968 * 969 * @see Builder#beanDefMapper(Class) 970 * @return 971 * Interface to use for converting Bean classes to definition IDs and URIs. 972 */ 973 protected final BeanDefMapper getBeanDefMapper() { 974 return beanDefMapperBean; 975 } 976 977 /** 978 * Ignore types from schema definitions. 979 * 980 * @see Builder#ignoreTypes(String...) 981 * @return 982 * Custom schema information for particular class types. 983 */ 984 public List<Pattern> getIgnoreTypes() { 985 return ulist(ignoreTypePatterns); 986 } 987 988 /** 989 * Use bean definitions. 990 * 991 * @see Builder#useBeanDefs() 992 * @return 993 * <jk>true</jk> if schemas on beans will be serialized with <js>'$ref'</js> tags. 994 */ 995 protected final boolean isUseBeanDefs() { 996 return useBeanDefs; 997 } 998 999 //----------------------------------------------------------------------------------------------------------------- 1000 // Extended metadata 1001 //----------------------------------------------------------------------------------------------------------------- 1002 1003 @Override 1004 public JsonSchemaClassMeta getJsonSchemaClassMeta(ClassMeta<?> cm) { 1005 JsonSchemaClassMeta m = jsonSchemaClassMetas.get(cm); 1006 if (m == null) { 1007 m = new JsonSchemaClassMeta(cm, this); 1008 jsonSchemaClassMetas.put(cm, m); 1009 } 1010 return m; 1011 } 1012 1013 @Override 1014 public JsonSchemaBeanPropertyMeta getJsonSchemaBeanPropertyMeta(BeanPropertyMeta bpm) { 1015 JsonSchemaBeanPropertyMeta m = jsonSchemaBeanPropertyMetas.get(bpm); 1016 if (m == null) { 1017 m = new JsonSchemaBeanPropertyMeta(bpm, this); 1018 jsonSchemaBeanPropertyMetas.put(bpm, m); 1019 } 1020 return m; 1021 } 1022 1023 //----------------------------------------------------------------------------------------------------------------- 1024 // Other methods 1025 //----------------------------------------------------------------------------------------------------------------- 1026 1027 /** 1028 * Returns <jk>true</jk> if the specified type is ignored. 1029 * 1030 * <p> 1031 * The type is ignored if it's specified in the {@link Builder#ignoreTypes(String...)} setting. 1032 * <br>Ignored types return <jk>null</jk> on the call to {@link JsonSchemaGeneratorSession#getSchema(ClassMeta)}. 1033 * 1034 * @param cm The type to check. 1035 * @return <jk>true</jk> if the specified type is ignored. 1036 */ 1037 public boolean isIgnoredType(ClassMeta<?> cm) { 1038 for (Pattern p : ignoreTypePatterns) 1039 if (p.matcher(cm.getSimpleName()).matches() || p.matcher(cm.getName()).matches()) 1040 return true; 1041 return false; 1042 } 1043 1044 //----------------------------------------------------------------------------------------------------------------- 1045 // Other methods 1046 //----------------------------------------------------------------------------------------------------------------- 1047 1048 @Override /* Context */ 1049 protected JsonMap properties() { 1050 return filteredMap() 1051 .append("useBeanDefs", useBeanDefs) 1052 .append("allowNestedExamples", allowNestedExamples) 1053 .append("allowNestedDescriptions", allowNestedDescriptions) 1054 .append("beanDefMapper", beanDefMapper) 1055 .append("addExamplesTo", addExamplesTo) 1056 .append("addDescriptionsTo", addDescriptionsTo) 1057 .append("ignoreTypes", ignoreTypes); 1058 } 1059}