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.marshall.html;
018
019import org.apache.juneau.commons.collections.*;
020import org.apache.juneau.marshall.*;
021import org.apache.juneau.marshall.json.*;
022import org.apache.juneau.marshall.jsonschema.*;
023
024/**
025 * Serializes POJO metamodels to HTML.
026 *
027 * <h5 class='topic'>Media types</h5>
028 *
029 * Handles <c>Accept</c> types:  <bc>text/html+schema</bc>
030 * <p>
031 * Produces <c>Content-Type</c> types:  <bc>text/html</bc>
032 *
033 * <h5 class='topic'>Description</h5>
034 *
035 * Essentially the same as {@link HtmlSerializer}, except serializes the POJO metamodel instead of the model itself.
036 *
037 * <p>
038 * Produces output that describes the POJO metamodel similar to an XML schema document.
039 *
040 * <p>
041 * The easiest way to create instances of this class is through the {@link HtmlSerializer#getSchemaSerializer()},
042 * which will create a schema serializer with the same settings as the originating serializer.
043 *
044 * <h5 class='section'>Notes:</h5><ul>
045 *    <li class='note'>This class is thread safe and reusable.
046 * </ul>
047 *
048 * <h5 class='section'>See Also:</h5><ul>
049 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlSupport">HTML</a>
050
051 * </ul>
052 */
053@SuppressWarnings({
054   "java:S110", // Inheritance depth acceptable for HtmlSchemaSerializer hierarchy
055   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
056   "java:S9149" // Per-format static factories intentionally shadow the parent's.
057})
058public class HtmlSchemaSerializer extends HtmlSerializer {
059
060   /**
061    * Builder class.
062    */
063   public static class Builder extends HtmlSerializer.Builder<Builder> {
064
065      private static final Cache<HashKey,HtmlSchemaSerializer> CACHE = Cache.of(HashKey.class, HtmlSchemaSerializer.class).build();
066
067      JsonSchemaGenerator.Builder generatorBuilder;
068
069      /**
070       * Constructor, default settings.
071       */
072      protected Builder() {
073         produces("text/html");
074         accept("text/html+schema");
075         generatorBuilder = JsonSchemaGenerator.create().marshallingContext(marshallingContext());
076      }
077
078      /**
079       * Copy constructor.
080       *
081       * @param copyFrom The builder to copy from.
082       */
083      protected Builder(Builder copyFrom) {
084         super(copyFrom);
085         generatorBuilder = copyFrom.generatorBuilder.copy().marshallingContext(marshallingContext());
086      }
087
088      /**
089       * Copy constructor.
090       *
091       * @param copyFrom The bean to copy from.
092       */
093      protected Builder(HtmlSchemaSerializer copyFrom) {
094         super(copyFrom);
095         generatorBuilder = copyFrom.generator.copy().marshallingContext(marshallingContext());
096      }
097
098      /**
099       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Add descriptions.
100       *
101       * <p>
102       * Identifies which categories of types that descriptions should be automatically added to generated schemas.
103       * <p>
104       * The description is the result of calling {@link ClassMeta#getName()}.
105       *
106       * <h5 class='section'>See Also:</h5><ul>
107       *    <li class='jm'>{@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#addDescriptionsTo(TypeCategory...) JsonSchemaGenerator.Builder#addDescriptionsTo(TypeCategory...)}
108       * </ul>
109       *
110       * @param values
111       *    The values to add to this setting.
112       *    <br>The default is an empty string.
113       * @return This object.
114       */
115      public Builder addDescriptionsTo(TypeCategory...values) {
116         generatorBuilder.addDescriptionsTo(values);
117         return this;
118      }
119
120      /**
121       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Add examples.
122       *
123       * <p>
124       * Identifies which categories of types that examples should be automatically added to generated schemas.
125       * <p>
126       * The examples come from calling {@link ClassMeta#getExample(MarshallingSession,JsonParserSession)} which in turn gets examples
127       * from the following:
128       * <ul class='javatree'>
129       *    <li class='ja'>{@link Example}
130       *    <li class='ja'>{@link Marshalled#example() Marshalled(example)}
131       * </ul>
132       *
133       * <h5 class='section'>See Also:</h5><ul>
134       *    <li class='jm'>{@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#addExamplesTo(TypeCategory...) JsonSchemaGenerator.Builder#addExamplesTo(TypeCategory...)}
135       * </ul>
136       *
137       * @param values
138       *    The values to add to this setting.
139       *    <br>The default is an empty string.
140       * @return This object.
141       */
142      public Builder addExamplesTo(TypeCategory...values) {
143         generatorBuilder.addExamplesTo(values);
144         return this;
145      }
146
147      /**
148       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Allow nested descriptions.
149       *
150       * <p>
151       * Identifies whether nested descriptions are allowed in schema definitions.
152       *
153       * <h5 class='section'>See Also:</h5><ul>
154       *    <li class='jm'>{@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedDescriptions() JsonSchemaGenerator.Builder#allowNestedDescriptions()}
155       * </ul>
156       *
157       * @return This object.
158       */
159      public Builder allowNestedDescriptions() {
160         generatorBuilder.allowNestedDescriptions();
161         return this;
162      }
163
164      /**
165       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Allow nested examples.
166       *
167       * <p>
168       * Identifies whether nested examples are allowed in schema definitions.
169       *
170       * <h5 class='section'>See Also:</h5><ul>
171       *    <li class='jm'>{@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#allowNestedExamples() JsonSchemaGenerator.Builder#allowNestedExamples()}
172       * </ul>
173       *
174       * @return This object.
175       */
176      public Builder allowNestedExamples() {
177         generatorBuilder.allowNestedExamples();
178         return this;
179      }
180
181      /**
182       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Schema definition mapper.
183       *
184       * <p>
185       * Interface to use for converting Bean classes to definition IDs and URIs.
186       * <p>
187       * Used primarily for defining common definition sections for beans in Swagger JSON.
188       * <p>
189       * This setting is ignored if {@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs() JsonSchemaGenerator.Builder#useBeanDefs()} is not enabled.
190       *
191       * <h5 class='section'>See Also:</h5><ul>
192       *    <li class='jm'>{@link org.apache.juneau.marshall.jsonschema.JsonSchemaGenerator.Builder#beanDefMapper(Class) JsonSchemaGenerator.Builder#beanDefMapper(Class)}
193       * </ul>
194       *
195       * @param value
196       *    The new value for this property.
197       *    <br>The default is {@link BasicBeanDefMapper}.
198       * @return This object.
199       */
200      public Builder beanDefMapper(Class<? extends MarshallingDefMapper> value) {
201         generatorBuilder.beanDefMapper(value);
202         return this;
203      }
204
205      @Override /* Overridden from Context.Builder<?> */
206      public HtmlSchemaSerializer build() {
207         return cache(CACHE).build(HtmlSchemaSerializer.class);
208      }
209
210      @Override /* Overridden from Context.Builder<?> */
211      public Builder copy() {
212         return new Builder(this);
213      }
214
215      @Override /* Overridden from Context.Builder<?> */
216      public HashKey hashKey() {
217         // @formatter:off
218         return HashKey.of(
219            super.hashKey(),
220            generatorBuilder.hashKey()
221         );
222         // @formatter:on
223      }
224
225      /**
226       * <i><l>HtmlSchemaSerializer</l> configuration property:&emsp;</i>  Use bean definitions.
227       *
228       * <p>
229       * When enabled, schemas on beans will be serialized as the following:
230       * <p class='bjson'>
231       *    {
232       *       type: <js>'object'</js>,
233       *       <js>'$ref'</js>: <js>'#/definitions/TypeId'</js>
234       *    }
235       * </p>
236       *
237       * @return This object.
238       */
239      public Builder useBeanDefs() {
240         generatorBuilder.useBeanDefs();
241         return this;
242      }
243
244
245   }
246
247   /** Default serializer, with whitespace. */
248   public static class Readable extends HtmlSchemaSerializer {
249
250      /**
251       * Constructor.
252       *
253       * @param builder The builder for this object.
254       */
255      public Readable(Builder builder) {
256         super(builder.useWhitespace());
257      }
258   }
259
260   /** Default serializer, single quotes, simple mode. */
261   public static class Simple extends HtmlSchemaSerializer {
262
263      /**
264       * Constructor.
265       *
266       * @param builder The builder for this object.
267       */
268      public Simple(Builder builder) {
269         super(builder.quoteChar('\''));
270      }
271   }
272
273   /** Default serializer, single quotes, simple mode, with whitespace. */
274   public static class SimpleReadable extends HtmlSchemaSerializer {
275
276      /**
277       * Constructor.
278       *
279       * @param builder The builder for this object.
280       */
281      public SimpleReadable(Builder builder) {
282         super(builder.quoteChar('\'').useWhitespace());
283      }
284   }
285
286   /** Default serializer, all default settings.*/
287   public static final HtmlSchemaSerializer DEFAULT = new HtmlSchemaSerializer(create());
288   /** Default serializer, all default settings.*/
289   public static final HtmlSchemaSerializer DEFAULT_READABLE = new Readable(create());
290
291   /** Default serializer, single quotes, simple mode. */
292   public static final HtmlSchemaSerializer DEFAULT_SIMPLE = new Simple(create());
293
294   /** Default serializer, single quotes, simple mode, with whitespace. */
295   public static final HtmlSchemaSerializer DEFAULT_SIMPLE_READABLE = new SimpleReadable(create());
296
297   /**
298    * Creates a new builder for this object.
299    *
300    * @return A new builder.
301    */
302   public static Builder create() {
303      return new Builder();
304   }
305
306   final JsonSchemaGenerator generator;
307
308   /**
309    * Constructor.
310    *
311    * @param builder The builder for this serializer.
312    */
313   public HtmlSchemaSerializer(Builder builder) {
314      super(builder.detectRecursions().ignoreRecursions());
315
316      generator = builder.generatorBuilder.build();
317   }
318
319   @Override /* Overridden from Context */
320   public Builder copy() {
321      return new Builder(this);
322   }
323
324   @Override /* Overridden from Context */
325   public HtmlSchemaSerializerSession.Builder createSession() {
326      return HtmlSchemaSerializerSession.create(this);
327   }
328
329   @Override /* Overridden from Context */
330   public HtmlSchemaSerializerSession getSession() { return createSession().build(); }
331
332   @Override /* Overridden from HtmlSerializer */
333   protected FluentMap<String,Object> properties() {
334      return super.properties()
335         .a("generator", generator);
336   }
337
338   JsonSchemaGenerator getGenerator() { return generator; }
339}