001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bean.jsonschema; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022import java.lang.reflect.*; 023 024import org.apache.juneau.marshall.collections.*; 025import org.apache.juneau.marshall.json.*; 026import org.apache.juneau.marshall.jsonschema.*; 027import org.apache.juneau.marshall.marshaller.Json; 028import org.apache.juneau.marshall.parser.*; 029 030/** 031 * Bridge for generating typed {@link JsonSchema} beans from Java types. 032 * 033 * <p> 034 * {@link JsonSchemaGenerator} in <c>juneau-marshall</c> produces <c>JsonMap</c> output. This class wraps that 035 * generator and converts the generated map into typed <c>JsonSchema</c> beans in <c>juneau-bean-jsonschema</c>. 036 * 037 * <h5 class='section'>Example:</h5> 038 * <p class='bjava'> 039 * <jc>// Default generation.</jc> 040 * JsonSchema <jv>s1</jv> = JsonSchemaBeanGenerator.<jsf>DEFAULT</jsf>.generate(MyBean.<jk>class</jk>); 041 * 042 * <jc>// Custom generation with bean defs and descriptions.</jc> 043 * JsonSchema <jv>s2</jv> = JsonSchemaBeanGenerator.<jsm>create</jsm>() 044 * .useBeanDefs() 045 * .addDescriptionsTo(TypeCategory.<jsf>ANY</jsf>) 046 * .build() 047 * .generate(MyBean.<jk>class</jk>); 048 * </p> 049 */ 050@SuppressWarnings({ 051 "java:S1452" // generate() and toBean() return JsonSchema<?> because the concrete SELF type is unknown to callers. 052}) 053public final class JsonSchemaBeanGenerator { 054 055 /** Reusable default instance. */ 056 public static final JsonSchemaBeanGenerator DEFAULT = create().build(); 057 058 /** Creates a new builder. */ 059 public static Builder create() { 060 return new Builder(); 061 } 062 063 private final JsonSchemaGenerator generator; 064 065 private JsonSchemaBeanGenerator(Builder builder) { 066 this.generator = builder.generatorBuilder.build(); 067 } 068 069 /** 070 * Generates a schema bean from a Java type. 071 * 072 * @param type The Java type. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 073 * @return The generated schema bean, or <jk>null</jk> if a schema could not be generated for the type. 074 */ 075 public JsonSchema<?> generate(Type type) { 076 reqnn("type", type); 077 try { 078 var session = generator.getSession(); 079 var root = session.getSchema(type); 080 if (root == null) 081 return null; 082 var defs = session.getBeanDefs(); 083 if (defs != null && ! defs.isEmpty()) 084 root.append("$defs", defs); 085 return toBean(root); 086 } catch (Exception e) { 087 throw toRex(e); 088 } 089 } 090 091 /** 092 * Generates a schema bean from a Java class. 093 * 094 * @param type The Java class. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 095 * @return The generated schema bean, or <jk>null</jk> if a schema could not be generated for the class. 096 */ 097 public JsonSchema<?> generate(Class<?> type) { 098 return generate((Type)type); 099 } 100 101 /** 102 * Generates a schema bean from an object. 103 * 104 * <p> 105 * The value can be a POJO or a <c>Class</c>/<c>Type</c>. 106 * 107 * @param o The value to infer a schema from. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 108 * @return The generated schema bean, or <jk>null</jk> if a schema could not be generated for the value. 109 */ 110 public JsonSchema<?> generate(Object o) { 111 reqnn("o", o); 112 try { 113 var session = generator.getSession(); 114 var root = session.getSchema(o); 115 if (root == null) 116 return null; 117 var defs = session.getBeanDefs(); 118 if (defs != null && ! defs.isEmpty()) 119 root.append("$defs", defs); 120 return toBean(root); 121 } catch (Exception e) { 122 throw toRex(e); 123 } 124 } 125 126 /** 127 * Converts a generated schema map into a typed bean. 128 * 129 * <p> 130 * Conversion uses a JSON roundtrip to ensure the swaps declared on {@link JsonSchema} are honored. 131 * 132 * @param schemaMap The generated schema map. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 133 * @return The typed schema bean. 134 */ 135 public static JsonSchema<?> toBean(JsonMap schemaMap) { 136 reqnn("schemaMap", schemaMap); 137 try { 138 var json = Json.of(schemaMap); 139 return JsonParser.create().ignoreUnknownBeanProperties().build().read(json, JsonSchema.class); 140 } catch (ParseException e) { 141 throw toRex(e); 142 } 143 } 144 145 /** Builder for {@link JsonSchemaBeanGenerator}. */ 146 public static final class Builder { 147 private final JsonSchemaGenerator.Builder generatorBuilder = JsonSchemaGenerator.create(); 148 149 private Builder() {} 150 151 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#addDescriptionsTo(TypeCategory...) JsonSchemaGenerator.Builder#addDescriptionsTo(TypeCategory...)}. */ 152 public Builder addDescriptionsTo(TypeCategory...values) { 153 generatorBuilder.addDescriptionsTo(values); 154 return this; 155 } 156 157 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#addExamplesTo(TypeCategory...) JsonSchemaGenerator.Builder#addExamplesTo(TypeCategory...)}. */ 158 public Builder addExamplesTo(TypeCategory...values) { 159 generatorBuilder.addExamplesTo(values); 160 return this; 161 } 162 163 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedDescriptions() JsonSchemaGenerator.Builder#allowNestedDescriptions()}. */ 164 public Builder allowNestedDescriptions() { 165 generatorBuilder.allowNestedDescriptions(); 166 return this; 167 } 168 169 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedDescriptions(boolean) JsonSchemaGenerator.Builder#allowNestedDescriptions(boolean)}. */ 170 public Builder allowNestedDescriptions(boolean value) { 171 generatorBuilder.allowNestedDescriptions(value); 172 return this; 173 } 174 175 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedExamples() JsonSchemaGenerator.Builder#allowNestedExamples()}. */ 176 public Builder allowNestedExamples() { 177 generatorBuilder.allowNestedExamples(); 178 return this; 179 } 180 181 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedExamples(boolean) JsonSchemaGenerator.Builder#allowNestedExamples(boolean)}. */ 182 public Builder allowNestedExamples(boolean value) { 183 generatorBuilder.allowNestedExamples(value); 184 return this; 185 } 186 187 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#beanDefMapper(Class) JsonSchemaGenerator.Builder#beanDefMapper(Class)}. */ 188 public Builder beanDefMapper(Class<? extends MarshallingDefMapper> value) { 189 generatorBuilder.beanDefMapper(value); 190 return this; 191 } 192 193 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#ignoreTypes(String...) JsonSchemaGenerator.Builder#ignoreTypes(String...)}. */ 194 public Builder ignoreTypes(String...values) { 195 generatorBuilder.ignoreTypes(values); 196 return this; 197 } 198 199 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs() JsonSchemaGenerator.Builder#useBeanDefs()}. */ 200 public Builder useBeanDefs() { 201 generatorBuilder.useBeanDefs(); 202 return this; 203 } 204 205 /** Mirrors {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs(boolean) JsonSchemaGenerator.Builder#useBeanDefs(boolean)}. */ 206 public Builder useBeanDefs(boolean value) { 207 generatorBuilder.useBeanDefs(value); 208 return this; 209 } 210 211 /** Builds a new generator. */ 212 public JsonSchemaBeanGenerator build() { 213 return new JsonSchemaBeanGenerator(this); 214 } 215 } 216}