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