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