This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a1d669143 feat(jsonschema): add typed JsonSchema bean generation 
bridge and TODO-8 docs/tests
8a1d669143 is described below

commit 8a1d6691435823032210fb75febdd8c0c7f3f0c6
Author: James Bognar <[email protected]>
AuthorDate: Fri May 15 13:34:21 2026 -0400

    feat(jsonschema): add typed JsonSchema bean generation bridge and TODO-8 
docs/tests
---
 .../apache/juneau/bean/jsonschema/JsonSchema.java  |  93 +++++++++
 .../bean/jsonschema/JsonSchemaBeanGenerator.java   | 212 +++++++++++++++++++++
 .../juneau/bean/jsonschema/JsonSchemaProperty.java |  18 ++
 .../juneau/bean/jsonschema/JsonSchemaRef.java      |  18 ++
 .../juneau/bean/jsonschema/package-info.java       |  10 +
 .../jsonschema/JsonSchemaBeanGenerator_Test.java   | 192 +++++++++++++++++++
 .../juneau/bean/jsonschema/JsonSchema_Test.java    |  21 ++
 ...13-system-properties-to-settings-conversion.md} |   2 +-
 ...FINISHED-21-bean-annotations-inject-package.md} |   0
 ...md => FINISHED-8-jsonschema-bean-generation.md} |   8 +
 todo/TODO.md                                       |   4 -
 11 files changed, 573 insertions(+), 5 deletions(-)

diff --git 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchema.java
 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchema.java
index 851d86cc76..2d8ca6a927 100644
--- 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchema.java
+++ 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchema.java
@@ -22,6 +22,7 @@ import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
 
+import java.lang.reflect.*;
 import java.net.*;
 import java.util.*;
 
@@ -103,6 +104,7 @@ import org.apache.juneau.swap.*;
  *     <li><b>Fluent API:</b> All setter methods return <c>this</c> for method 
chaining
  *     <li><b>Type Safety:</b> Uses enums and typed collections for validation
  *     <li><b>Serialization:</b> Can be serialized to any format supported by 
Juneau (JSON, XML, HTML, etc.)
+ *     <li><b>Auto Generation:</b> Use {@link JsonSchemaBeanGenerator} to 
generate schemas from Java types
  * </ul>
  *
  * <h5 class='section'>Common Use Cases:</h5>
@@ -362,6 +364,9 @@ public class JsonSchema {
        private JsonSchema else_;                              // Draft 07+
        private Boolean readOnly;                              // Draft 07+
        private Boolean writeOnly;                             // Draft 07+
+       private String format;                                 // 
Annotation-level keyword
+       private String comment;                                // Draft 07+, 
serialized as $comment
+       private Boolean deprecated;                            // Draft 2019-09+
        private String contentMediaType;                       // Draft 07+
 
        private String contentEncoding;                        // Draft 07+
@@ -378,6 +383,26 @@ public class JsonSchema {
         */
        public JsonSchema() { /* Empty constructor. */ }
 
+       /**
+        * Generates a {@link JsonSchema} bean from the specified type using 
{@link JsonSchemaBeanGenerator#DEFAULT}.
+        *
+        * @param type The type to generate a schema for.
+        * @return The generated schema bean.
+        */
+       public static JsonSchema of(Type type) {
+               return JsonSchemaBeanGenerator.DEFAULT.generate(type);
+       }
+
+       /**
+        * Generates a {@link JsonSchema} bean from the specified class using 
{@link JsonSchemaBeanGenerator#DEFAULT}.
+        *
+        * @param type The class to generate a schema for.
+        * @return The generated schema bean.
+        */
+       public static JsonSchema of(Class<?> type) {
+               return JsonSchemaBeanGenerator.DEFAULT.generate(type);
+       }
+
        /**
         * Bean property appender:  <property>additionalItems</property>.
         *
@@ -785,6 +810,27 @@ public class JsonSchema {
         */
        public String getContentMediaType() { return contentMediaType; }
 
+       /**
+        * Bean property getter:  <property>deprecated</property>.
+        *
+        * <p>
+        * This property was added in Draft 2019-09.
+        *
+        * @return The value of the <property>deprecated</property> property on 
this bean, or <jk>null</jk> if it is not set.
+        */
+       public Boolean getDeprecated() { return deprecated; }
+
+       /**
+        * Bean property getter:  <property>$comment</property>.
+        *
+        * <p>
+        * This property was added in Draft 07.
+        *
+        * @return The value of the <property>$comment</property> property on 
this bean, or <jk>null</jk> if it is not set.
+        */
+       @BeanProp("$comment")
+       public String getComment() { return comment; }
+
        /**
         * Bean property getter:  <property>definitions</property>.
         *
@@ -821,6 +867,13 @@ public class JsonSchema {
         */
        public Map<String,JsonSchema> getDependencies() { return dependencies; }
 
+       /**
+        * Bean property getter:  <property>format</property>.
+        *
+        * @return The value of the <property>format</property> property on 
this bean, or <jk>null</jk> if it is not set.
+        */
+       public String getFormat() { return format; }
+
        /**
         * Bean property getter:  <property>dependentRequired</property>.
         *
@@ -1402,6 +1455,35 @@ public class JsonSchema {
                return this;
        }
 
+       /**
+        * Bean property setter:  <property>deprecated</property>.
+        *
+        * <p>
+        * This property was added in Draft 2019-09.
+        *
+        * @param value The new value for the <property>deprecated</property> 
property on this bean.
+        * @return This object.
+        */
+       public JsonSchema setDeprecated(Boolean value) {
+               this.deprecated = value;
+               return this;
+       }
+
+       /**
+        * Bean property setter:  <property>$comment</property>.
+        *
+        * <p>
+        * This property was added in Draft 07.
+        *
+        * @param value The new value for the <property>$comment</property> 
property on this bean.
+        * @return This object.
+        */
+       @BeanProp("$comment")
+       public JsonSchema setComment(String value) {
+               this.comment = value;
+               return this;
+       }
+
        /**
         * Bean property setter:  <property>definitions</property>.
         *
@@ -1446,6 +1528,17 @@ public class JsonSchema {
                return this;
        }
 
+       /**
+        * Bean property setter:  <property>format</property>.
+        *
+        * @param value The new value for the <property>format</property> 
property on this bean.
+        * @return This object.
+        */
+       public JsonSchema setFormat(String value) {
+               this.format = value;
+               return this;
+       }
+
        /**
         * Bean property setter:  <property>dependentRequired</property>.
         *
diff --git 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator.java
 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator.java
new file mode 100644
index 0000000000..89eebfbde4
--- /dev/null
+++ 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.bean.jsonschema;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.ThrowableUtils.*;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.collections.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.jsonschema.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Bridge for generating typed {@link JsonSchema} beans from Java types.
+ *
+ * <p>
+ * {@link JsonSchemaGenerator} in <c>juneau-marshall</c> produces 
<c>JsonMap</c> output.  This class wraps that
+ * generator and converts the generated map into typed <c>JsonSchema</c> beans 
in <c>juneau-bean-jsonschema</c>.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Default generation.</jc>
+ *     JsonSchema <jv>s1</jv> = 
JsonSchemaBeanGenerator.<jsf>DEFAULT</jsf>.generate(MyBean.<jk>class</jk>);
+ *
+ *     <jc>// Custom generation with bean defs and descriptions.</jc>
+ *     JsonSchema <jv>s2</jv> = JsonSchemaBeanGenerator.<jsm>create</jsm>()
+ *             .useBeanDefs()
+ *             .addDescriptionsTo(TypeCategory.<jsf>ANY</jsf>)
+ *             .build()
+ *             .generate(MyBean.<jk>class</jk>);
+ * </p>
+ */
+public final class JsonSchemaBeanGenerator {
+
+       /** Reusable default instance. */
+       public static final JsonSchemaBeanGenerator DEFAULT = create().build();
+
+       /** Creates a new builder. */
+       public static Builder create() {
+               return new Builder();
+       }
+
+       private final JsonSchemaGenerator generator;
+
+       private JsonSchemaBeanGenerator(Builder builder) {
+               this.generator = builder.generatorBuilder.build();
+       }
+
+       /**
+        * Generates a schema bean from a Java type.
+        *
+        * @param type The Java type.
+        * @return The generated schema bean.
+        */
+       public JsonSchema generate(Type type) {
+               assertArgNotNull("type", type);
+               try {
+                       var session = generator.getSession();
+                       var root = session.getSchema(type);
+                       if (root == null)
+                               return null;
+                       var defs = session.getBeanDefs();
+                       if (defs != null && ! defs.isEmpty())
+                               root.append("$defs", defs);
+                       return toBean(root);
+               } catch (Exception e) {
+                       throw toRex(e);
+               }
+       }
+
+       /**
+        * Generates a schema bean from a Java class.
+        *
+        * @param type The Java class.
+        * @return The generated schema bean.
+        */
+       public JsonSchema generate(Class<?> type) {
+               return generate((Type)type);
+       }
+
+       /**
+        * Generates a schema bean from an object.
+        *
+        * <p>
+        * The value can be a POJO or a <c>Class</c>/<c>Type</c>.
+        *
+        * @param o The value to infer a schema from.
+        * @return The generated schema bean.
+        */
+       public JsonSchema generate(Object o) {
+               assertArgNotNull("o", o);
+               try {
+                       var session = generator.getSession();
+                       var root = session.getSchema(o);
+                       if (root == null)
+                               return null;
+                       var defs = session.getBeanDefs();
+                       if (defs != null && ! defs.isEmpty())
+                               root.append("$defs", defs);
+                       return toBean(root);
+               } catch (Exception e) {
+                       throw toRex(e);
+               }
+       }
+
+       /**
+        * Converts a generated schema map into a typed bean.
+        *
+        * <p>
+        * Conversion uses a JSON roundtrip to ensure the swaps declared on 
{@link JsonSchema} are honored.
+        *
+        * @param schemaMap The generated schema map.
+        * @return The typed schema bean.
+        */
+       public static JsonSchema toBean(JsonMap schemaMap) {
+               assertArgNotNull("schemaMap", schemaMap);
+               try {
+                       var json = JsonSerializer.DEFAULT.toString(schemaMap);
+                       return 
JsonParser.create().ignoreUnknownBeanProperties().build().parse(json, 
JsonSchema.class);
+               } catch (ParseException e) {
+                       throw toRex(e);
+               }
+       }
+
+       /** Builder for {@link JsonSchemaBeanGenerator}. */
+       public static final class Builder {
+               private final JsonSchemaGenerator.Builder generatorBuilder = 
JsonSchemaGenerator.create();
+
+               private Builder() {}
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#addDescriptionsTo(TypeCategory...)}.
 */
+               public Builder addDescriptionsTo(TypeCategory...values) {
+                       generatorBuilder.addDescriptionsTo(values);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#addExamplesTo(TypeCategory...)}.
 */
+               public Builder addExamplesTo(TypeCategory...values) {
+                       generatorBuilder.addExamplesTo(values);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#allowNestedDescriptions()}.
 */
+               public Builder allowNestedDescriptions() {
+                       generatorBuilder.allowNestedDescriptions();
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#allowNestedDescriptions(boolean)}.
 */
+               public Builder allowNestedDescriptions(boolean value) {
+                       generatorBuilder.allowNestedDescriptions(value);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#allowNestedExamples()}.
 */
+               public Builder allowNestedExamples() {
+                       generatorBuilder.allowNestedExamples();
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#allowNestedExamples(boolean)}.
 */
+               public Builder allowNestedExamples(boolean value) {
+                       generatorBuilder.allowNestedExamples(value);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#beanDefMapper(Class)}. 
*/
+               public Builder beanDefMapper(Class<? extends 
MarshallingDefMapper> value) {
+                       generatorBuilder.beanDefMapper(value);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#ignoreTypes(String...)}.
 */
+               public Builder ignoreTypes(String...values) {
+                       generatorBuilder.ignoreTypes(values);
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs()}. */
+               public Builder useBeanDefs() {
+                       generatorBuilder.useBeanDefs();
+                       return this;
+               }
+
+               /** Mirrors {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs(boolean)}. 
*/
+               public Builder useBeanDefs(boolean value) {
+                       generatorBuilder.useBeanDefs(value);
+                       return this;
+               }
+
+               /** Builds a new generator. */
+               public JsonSchemaBeanGenerator build() {
+                       return new JsonSchemaBeanGenerator(this);
+               }
+       }
+}
diff --git 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaProperty.java
 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaProperty.java
index 14664ffd84..ee81d26120 100644
--- 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaProperty.java
+++ 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaProperty.java
@@ -192,6 +192,18 @@ public class JsonSchemaProperty extends JsonSchema {
                return this;
        }
 
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaProperty setDeprecated(Boolean value) {
+               super.setDeprecated(value);
+               return this;
+       }
+
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaProperty setComment(String value) {
+               super.setComment(value);
+               return this;
+       }
+
        @Override /* Overridden from JsonSchema */
        public JsonSchemaProperty setDefinitions(Map<String,JsonSchema> value) {
                super.setDefinitions(value);
@@ -222,6 +234,12 @@ public class JsonSchemaProperty extends JsonSchema {
                return this;
        }
 
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaProperty setFormat(String value) {
+               super.setFormat(value);
+               return this;
+       }
+
        @Override /* Overridden from JsonSchema */
        public JsonSchemaProperty setEnum(List<Object> value) {
                super.setEnum(value);
diff --git 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaRef.java
 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaRef.java
index abe0241f64..1b01676b44 100644
--- 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaRef.java
+++ 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/JsonSchemaRef.java
@@ -193,6 +193,18 @@ public class JsonSchemaRef extends JsonSchema {
                return this;
        }
 
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaRef setDeprecated(Boolean value) {
+               super.setDeprecated(value);
+               return this;
+       }
+
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaRef setComment(String value) {
+               super.setComment(value);
+               return this;
+       }
+
        @Override /* Overridden from JsonSchema */
        public JsonSchemaRef setDefinitions(Map<String,JsonSchema> value) {
                super.setDefinitions(value);
@@ -223,6 +235,12 @@ public class JsonSchemaRef extends JsonSchema {
                return this;
        }
 
+       @Override /* Overridden from JsonSchema */
+       public JsonSchemaRef setFormat(String value) {
+               super.setFormat(value);
+               return this;
+       }
+
        @Override /* Overridden from JsonSchema */
        public JsonSchemaRef setEnum(List<Object> value) {
                super.setEnum(value);
diff --git 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/package-info.java
 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/package-info.java
index 57ec89ce0c..8c087c243c 100644
--- 
a/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/package-info.java
+++ 
b/juneau-bean/juneau-bean-jsonschema/src/main/java/org/apache/juneau/bean/jsonschema/package-info.java
@@ -47,7 +47,17 @@
  *     <li>{@link org.apache.juneau.bean.jsonschema.JsonSchemaArray} - An 
array of <c>JsonSchema</c> objects.
  *     <li>{@link org.apache.juneau.bean.jsonschema.JsonType} - An enum of 
possible JSON data types.
  *     <li>{@link org.apache.juneau.bean.jsonschema.JsonTypeArray} - An array 
of <c>JsonType</c> objects.
+ *     <li>{@link org.apache.juneau.bean.jsonschema.JsonSchemaBeanGenerator} - 
Generates typed
+ *             <c>JsonSchema</c> beans from Java types via {@link 
org.apache.juneau.jsonschema.JsonSchemaGenerator}.
  * </ul>
+
+ * <h5 class='topic'>Automatic Schema Generation</h5>
+ *
+ * <p>
+ * Use {@link org.apache.juneau.bean.jsonschema.JsonSchemaBeanGenerator} when 
you want typed DTO output from POJOs
+ * without manually building schema trees.  It bridges the map-based output of
+ * {@link org.apache.juneau.jsonschema.JsonSchemaGenerator} into 
<c>JsonSchema</c> beans.
+ * </p>
  *
  * <h5 class='topic'>Creating JSON Schema Documents</h5>
  *
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator_Test.java
new file mode 100644
index 0000000000..edcf88238a
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchemaBeanGenerator_Test.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.bean.jsonschema;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.jsonschema.*;
+import org.junit.jupiter.api.*;
+
+class JsonSchemaBeanGenerator_Test extends TestBase {
+
+       @Test void a01_primitives() {
+               var g = JsonSchemaBeanGenerator.DEFAULT;
+               assertEquals("integer", 
g.generate(int.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("int32", g.generate(int.class).getFormat());
+               assertEquals("number", 
g.generate(float.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("float", g.generate(float.class).getFormat());
+               assertEquals("boolean", 
g.generate(boolean.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("string", 
g.generate(String.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("uri", g.generate(URI.class).getFormat());
+       }
+
+       @Test void a02_bean_collection_map_enum() {
+               var g = JsonSchemaBeanGenerator.DEFAULT;
+               assertEquals("object", 
g.generate(SimpleBean.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertNotNull(g.generate(SimpleBean.class).getProperty("name"));
+               assertEquals("array", 
g.generate(BeanList.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("object", 
g.generate(BeanList.class).getItemsAsSchema().getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals(Boolean.TRUE, 
g.generate(BeanSet.class).getUniqueItems());
+               
assertNotNull(g.generate(BeanMap.class).getAdditionalPropertiesAsSchema());
+               assertEquals("string", 
g.generate(TinyEnum.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals(List.of("one","two"), 
g.generate(TinyEnum.class).getEnum());
+       }
+
+       @Test void a03_useBeanDefs_injectsDefsAtRoot() {
+               var bean = 
JsonSchemaBeanGenerator.create().useBeanDefs().build().generate(SimpleBean.class);
+               assertEquals("#/definitions/SimpleBean", 
bean.getRef().toString());
+               assertNotNull(bean.getDefs());
+               assertTrue(bean.getDefs().containsKey("SimpleBean"));
+       }
+
+       @Test void a05_descriptions_and_examples() {
+               var bean = JsonSchemaBeanGenerator.create()
+                       .addDescriptionsTo(TypeCategory.ANY)
+                       .addExamplesTo(TypeCategory.ANY)
+                       .allowNestedDescriptions()
+                       .allowNestedExamples()
+                       .build()
+                       .generate(ExampleBean.class);
+
+               assertEquals("object", 
bean.getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               
assertEquals("org.apache.juneau.bean.jsonschema.JsonSchemaBeanGenerator_Test$ExampleBean",
 bean.getDescription());
+               assertEquals("java.lang.String", 
bean.getProperty("value").getDescription());
+       }
+
+       @Test void a06_roundTripParityWithJsonMapOutput() throws Exception {
+               var map = 
JsonSchemaGenerator.DEFAULT.getSession().getSchema(SimpleBean.class);
+               var bean = JsonSchemaBeanGenerator.toBean(map);
+               var mapJson = JsonSerializer.DEFAULT.serialize(map);
+               var beanJson = JsonSerializer.DEFAULT.serialize(bean);
+               assertEquals(
+                       JsonParser.DEFAULT.parse(mapJson, JsonMap.class),
+                       JsonParser.DEFAULT.parse(beanJson, JsonMap.class)
+               );
+       }
+
+       @Test void a07_staticOfFactories() {
+               assertEquals("object", 
JsonSchema.of(SimpleBean.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               
assertNotNull(JsonSchema.of(SimpleBean.class).getProperty("name"));
+               assertEquals("object", 
JsonSchema.of((java.lang.reflect.Type)SimpleBean.class).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               
assertNotNull(JsonSchema.of((java.lang.reflect.Type)SimpleBean.class).getProperty("name"));
+       }
+
+       @Test void a08_toBeanConversion() {
+               var map = 
JsonMap.ofJson("{type:'integer',format:'int32','$comment':'c',deprecated:true}");
+               var bean = JsonSchemaBeanGenerator.toBean(map);
+               assertEquals("integer", 
bean.getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertEquals("int32", bean.getFormat());
+               assertEquals("c", bean.getComment());
+               assertEquals(Boolean.TRUE, bean.getDeprecated());
+       }
+
+       @Test void b01_generateObjectPathAndIgnoredTypeNull() {
+               var g = JsonSchemaBeanGenerator.DEFAULT;
+               assertEquals("object", g.generate(new 
SimpleBean()).getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+       }
+
+       @Test void b01a_generateIgnoredTypeReturnsNull() {
+               var ignored = 
JsonSchemaBeanGenerator.create().ignoreTypes("SimpleBean").build();
+               assertNull(ignored.generate(SimpleBean.class));
+               assertNull(ignored.generate(new SimpleBean()));
+       }
+
+       @Test void b01b_generateObjectWithBeanDefsAddsDefs() {
+               var bean = 
JsonSchemaBeanGenerator.create().useBeanDefs().build().generate(new 
SimpleBean());
+               assertNotNull(bean.getDefs());
+               assertTrue(bean.getDefs().containsKey("SimpleBean"));
+       }
+
+       @Test void b01c_generateTypeWithBeanDefsButNoDefinitions() {
+               var bean = 
JsonSchemaBeanGenerator.create().useBeanDefs().build().generate(int.class);
+               assertEquals("integer", 
bean.getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertNull(bean.getDefs());
+       }
+
+       @Test void b01d_generateObjectWithBeanDefsButNoDefinitions() {
+               var bean = 
JsonSchemaBeanGenerator.create().useBeanDefs().build().generate(1);
+               assertEquals("integer", 
bean.getTypeAsJsonType().toString().toLowerCase(Locale.ROOT));
+               assertNull(bean.getDefs());
+       }
+
+       @Test void b02_builderDelegates() {
+               var g = JsonSchemaBeanGenerator.create()
+                       .addDescriptionsTo(TypeCategory.ANY)
+                       .addExamplesTo(TypeCategory.ANY)
+                       .allowNestedDescriptions()
+                       .allowNestedDescriptions(false)
+                       .allowNestedExamples()
+                       .allowNestedExamples(false)
+                       .beanDefMapper(TestBeanDefMapper.class)
+                       .ignoreTypes("com.example.DoesNotExist")
+                       .useBeanDefs()
+                       .useBeanDefs(false)
+                       .build();
+               assertNotNull(g.generate(SimpleBean.class));
+       }
+
+       @Test void b03_argumentValidation() {
+               assertThrows(RuntimeException.class, () -> 
JsonSchemaBeanGenerator.DEFAULT.generate((Type)null));
+               assertThrows(RuntimeException.class, () -> 
JsonSchemaBeanGenerator.DEFAULT.generate((Object)null));
+               assertThrows(RuntimeException.class, () -> 
JsonSchemaBeanGenerator.toBean(null));
+       }
+
+       public static class SimpleBean {
+               public String name;
+       }
+
+       public static class ExampleBean {
+               public String value;
+
+               @Example
+               public static ExampleBean example() {
+                       var x = new ExampleBean();
+                       x.value = "v";
+                       return x;
+               }
+       }
+
+       public static class BeanList extends LinkedList<SimpleBean> {
+               private static final long serialVersionUID = 1L;
+       }
+       public static class BeanSet extends LinkedHashSet<String> {
+               private static final long serialVersionUID = 1L;
+       }
+       public static class BeanMap extends LinkedHashMap<String,SimpleBean> {
+               private static final long serialVersionUID = 1L;
+       }
+
+       public static class TestBeanDefMapper extends BasicBeanDefMapper {}
+
+       public enum TinyEnum {
+               ONE,
+               TWO;
+
+               @Override
+               public String toString() {
+                       return name().toLowerCase(Locale.ROOT);
+               }
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchema_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchema_Test.java
index 63a70cfce8..88d879ec28 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchema_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/jsonschema/JsonSchema_Test.java
@@ -568,4 +568,25 @@ public class JsonSchema_Test extends TestBase {
                // resolve = true path (no schemaMap, so resolve() returns self)
                assertNotNull(x.getProperty("foo", true));
        }
+
+       @Test void b25_formatCommentDeprecated_roundTrip() throws Exception {
+               var s = Json5Serializer.create().ws().build();
+               var p = Json5Parser.DEFAULT;
+
+               var x = new JsonSchema()
+                       .setType(JsonType.STRING)
+                       .setFormat("uri")
+                       .setComment("schema note")
+                       .setDeprecated(true);
+
+               var r = s.serialize(x);
+               assertTrue(r.contains("format: 'uri'"));
+               assertTrue(r.contains("'$comment': 'schema note'"));
+               assertTrue(r.contains("deprecated: true"));
+
+               var x2 = p.parse(r, JsonSchema.class);
+               assertEquals("uri", x2.getFormat());
+               assertEquals("schema note", x2.getComment());
+               assertEquals(Boolean.TRUE, x2.getDeprecated());
+       }
 }
\ No newline at end of file
diff --git a/todo/TODO-13-system-properties-to-settings-conversion.md 
b/todo/FINISHED-13-system-properties-to-settings-conversion.md
similarity index 99%
rename from todo/TODO-13-system-properties-to-settings-conversion.md
rename to todo/FINISHED-13-system-properties-to-settings-conversion.md
index 83194dbf45..4de241a74c 100644
--- a/todo/TODO-13-system-properties-to-settings-conversion.md
+++ b/todo/FINISHED-13-system-properties-to-settings-conversion.md
@@ -120,7 +120,7 @@ Verify `Utils.env(name, CacheMode.FULL)` works (Settings 
supports Enum via value
 ### Phase 3: Special Cases
 
 8. **ParameterInfo.java** - Uses `ResettableSupplier` for testability. Options:
-   - Replace with 
`Settings.get().get("juneau.disableParamNameDetection").asBoolean()` 
+   - Replace with 
`Settings.get().get("juneau.disableParamNameDetection").asBoolean()`
    - Use `Setting.reset()` when test needs to re-read - check if Setting has 
reset
    - Keep ResettableSupplier but have it delegate to Settings
 
diff --git a/todo/TODO-21-bean-annotations-inject-package.md 
b/todo/FINISHED-21-bean-annotations-inject-package.md
similarity index 100%
rename from todo/TODO-21-bean-annotations-inject-package.md
rename to todo/FINISHED-21-bean-annotations-inject-package.md
diff --git a/todo/TODO-8-jsonschema-bean-generation.md 
b/todo/FINISHED-8-jsonschema-bean-generation.md
similarity index 97%
rename from todo/TODO-8-jsonschema-bean-generation.md
rename to todo/FINISHED-8-jsonschema-bean-generation.md
index f38c6a6df7..65ad5e662d 100644
--- a/todo/TODO-8-jsonschema-bean-generation.md
+++ b/todo/FINISHED-8-jsonschema-bean-generation.md
@@ -4,6 +4,14 @@
 
 Add the ability for `JsonSchemaGenerator` to produce typed `JsonSchema` beans 
instead of only `JsonMap` objects. This requires filling gaps in the 
`JsonSchema` bean, creating a bridge class in the `juneau-bean-jsonschema` 
module (which already depends on `juneau-marshall`), and comprehensive testing.
 
+## Execution Status
+
+- [x] Phase 1: Add Missing Properties to JsonSchema Bean
+- [x] Phase 2: Create JsonSchemaBeanGenerator
+- [x] Phase 3: Testing
+- [x] Phase 4: Documentation
+- [x] Phase 5: Verify and archive
+
 ## Problem
 
 `JsonSchemaGenerator` (in `juneau-marshall`) produces `JsonMap` objects. There 
is no way to obtain typed `JsonSchema` beans (in `juneau-bean-jsonschema`) from 
the generator. Users who want structured, type-safe schema objects must 
manually construct them or do ad-hoc conversion.
diff --git a/todo/TODO.md b/todo/TODO.md
index 5fb2d9a748..65e8153ee5 100644
--- a/todo/TODO.md
+++ b/todo/TODO.md
@@ -11,8 +11,6 @@
 
 - [TODO-7] Decouple `juneau-rest-common` from `juneau-marshall` by breaking 
the compile dependency so REST annotations and beans can be used without 
pulling in the full serialization stack. See 
`todo/TODO-7-decouple-rest-common-from-marshall.md`.
 
-- [TODO-8] Add typed `JsonSchema` bean output to `JsonSchemaGenerator` 
(currently returns only `JsonMap`). Requires filling gaps in the `JsonSchema` 
bean and adding a bridge class in `juneau-bean-jsonschema`. See 
`todo/TODO-8-jsonschema-bean-generation.md`.
-
 - [TODO-9] Fix remaining skipped Markdown round-trip test cases (tables, 
nested structures, edge cases). See `todo/TODO-9-markdown-remaining-issues.md`.
 
 - [TODO-10] Move `org.apache.juneau.http.annotation` from `juneau-marshall` 
into `juneau-rest-common` (already done for the annotation classes — plan 
tracks remaining follow-on cleanup). See 
`todo/TODO-10-move-http-annotation-to-rest-common.md`.
@@ -27,5 +25,3 @@
 
 - [TODO-30] Investigate moving `ClassMeta` and related non-marshalling type 
metadata from `juneau-marshall` into `juneau-commons` (analysis/feasibility 
pass). See `todo/TODO-30-classmeta-to-commons.md`.
 
-- [TODO-32] YAML support in juneau-config: add a YAML-format alternative to 
the existing INI-style Config. New `ConfigStore` implementation reading/writing 
`.yml` / `.yaml` files with parity for sections, keys, defaults, comments, and 
SVL interpolation; round-trips edits without losing comments where possible.
-

Reply via email to