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.openapi3;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.marshall.internal.ConverterUtils.*;
021
022import java.util.*;
023
024import org.apache.juneau.commons.collections.*;
025
026/**
027 * Holds a set of reusable objects for different aspects of the OpenAPI Specification.
028 *
029 * <p>
030 * The Components Object holds a set of reusable objects that can be referenced from other parts of the API specification.
031 * This promotes reusability and reduces duplication by allowing common schemas, responses, parameters, and other objects
032 * to be defined once and referenced multiple times using the <c>$ref</c> syntax.
033 *
034 * <h5 class='section'>OpenAPI Specification:</h5>
035 * <p>
036 * The Components Object is composed of the following fields:
037 * <ul class='spaced-list'>
038 *    <li><c>schemas</c> (map of {@link SchemaInfo}) - Reusable schema definitions
039 *    <li><c>responses</c> (map of {@link Response}) - Reusable response definitions
040 *    <li><c>parameters</c> (map of {@link Parameter}) - Reusable parameter definitions
041 *    <li><c>examples</c> (map of {@link Example}) - Reusable example definitions
042 *    <li><c>requestBodies</c> (map of {@link RequestBodyInfo}) - Reusable request body definitions
043 *    <li><c>headers</c> (map of {@link HeaderInfo}) - Reusable header definitions
044 *    <li><c>securitySchemes</c> (map of {@link SecuritySchemeInfo}) - Reusable security scheme definitions
045 *    <li><c>links</c> (map of {@link Link}) - Reusable link definitions
046 *    <li><c>callbacks</c> (map of {@link Callback}) - Reusable callback definitions
047 * </ul>
048 *
049 * <h5 class='section'>Example:</h5>
050 * <p class='bjava'>
051 *    <jc>// Create a Components object with reusable schemas</jc>
052 *    Components <jv>components</jv> = <jk>new</jk> Components()
053 *       .setSchemas(
054 *          JsonMap.<jsm>of</jsm>(
055 *             <js>"Pet"</js>, <jk>new</jk> SchemaInfo()
056 *                .setType(<js>"object"</js>)
057 *                .setRequired(<js>"id"</js>, <js>"name"</js>)
058 *                .setProperties(
059 *                   JsonMap.<jsm>of</jsm>(
060 *                      <js>"id"</js>, <jk>new</jk> SchemaInfo().setType(<js>"integer"</js>),
061 *                      <js>"name"</js>, <jk>new</jk> SchemaInfo().setType(<js>"string"</js>)
062 *                   )
063 *                ),
064 *             <js>"Error"</js>, <jk>new</jk> SchemaInfo()
065 *                .setType(<js>"object"</js>)
066 *                .setProperties(
067 *                   JsonMap.<jsm>of</jsm>(
068 *                      <js>"code"</js>, <jk>new</jk> SchemaInfo().setType(<js>"integer"</js>),
069 *                      <js>"message"</js>, <jk>new</jk> SchemaInfo().setType(<js>"string"</js>)
070 *                   )
071 *                )
072 *          )
073 *       );
074 *    <jc>// These schemas can then be referenced: "#/components/schemas/Pet"</jc>
075 * </p>
076 *
077 * <h5 class='section'>See Also:</h5><ul>
078 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#components-object">OpenAPI Specification &gt; Components Object</a>
079 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/components/">OpenAPI Components</a>
080 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
081 * </ul>
082 */
083@SuppressWarnings({
084   "java:S1192" // Duplicated literals (property names) read more clearly inline than as constants
085})
086public class Components extends OpenApiElement {
087
088   private Map<String,SchemaInfo> schemas;
089   private Map<String,Response> responses;
090   private Map<String,Parameter> parameters;
091   private Map<String,Example> examples;
092   private Map<String,RequestBodyInfo> requestBodies;
093   private Map<String,HeaderInfo> headers;
094   private Map<String,SecuritySchemeInfo> securitySchemes;
095   private Map<String,Link> links;
096   private Map<String,Callback> callbacks;
097
098   /**
099    * Default constructor.
100    */
101   public Components() {}
102
103   /**
104    * Copy constructor.
105    *
106    * @param copyFrom The object to copy.
107    */
108   public Components(Components copyFrom) {
109      super(copyFrom);
110      this.schemas = cp(copyFrom.schemas, SchemaInfo::copy);
111      this.responses = cp(copyFrom.responses, Response::copy);
112      this.parameters = cp(copyFrom.parameters, Parameter::copy);
113      this.examples = cp(copyFrom.examples, Example::copy);
114      this.requestBodies = cp(copyFrom.requestBodies, RequestBodyInfo::copy);
115      this.headers = cp(copyFrom.headers, HeaderInfo::copy);
116      this.securitySchemes = cp(copyFrom.securitySchemes, SecuritySchemeInfo::copy);
117      this.links = cp(copyFrom.links, Link::copy);
118      this.callbacks = cp(copyFrom.callbacks, Callback::copy);
119   }
120
121   /**
122    * Creates a copy of this object.
123    *
124    * @return A copy of this object.
125    */
126   public Components copy() {
127      return new Components(this);
128   }
129
130   @Override /* Overridden from OpenApiElement */
131   public <T> T get(String property, Class<T> type) {
132      reqnn("property", property);
133      return switch (property) {
134         case "schemas" -> toType(getSchemas(), type);
135         case "responses" -> toType(getResponses(), type);
136         case "parameters" -> toType(getParameters(), type);
137         case "examples" -> toType(getExamples(), type);
138         case "requestBodies" -> toType(getRequestBodies(), type);
139         case "headers" -> toType(getHeaders(), type);
140         case "securitySchemes" -> toType(getSecuritySchemes(), type);
141         case "links" -> toType(getLinks(), type);
142         case "callbacks" -> toType(getCallbacks(), type);
143         default -> super.get(property, type);
144      };
145   }
146
147   /**
148    * Returns the callbacks map.
149    *
150    * @return The callbacks map, or <jk>null</jk> if not set.
151    */
152   public Map<String,Callback> getCallbacks() { return u(callbacks); }
153
154   /**
155    * Returns the examples map.
156    *
157    * @return The examples map, or <jk>null</jk> if not set.
158    */
159   public Map<String,Example> getExamples() { return u(examples); }
160
161   /**
162    * Returns the headers map.
163    *
164    * @return The headers map, or <jk>null</jk> if not set.
165    */
166   public Map<String,HeaderInfo> getHeaders() { return u(headers); }
167
168   /**
169    * Returns the links map.
170    *
171    * @return The links map, or <jk>null</jk> if not set.
172    */
173   public Map<String,Link> getLinks() { return u(links); }
174
175   /**
176    * Returns the parameters map.
177    *
178    * @return The parameters map, or <jk>null</jk> if not set.
179    */
180   public Map<String,Parameter> getParameters() { return u(parameters); }
181
182   /**
183    * Returns the request bodies map.
184    *
185    * @return The request bodies map, or <jk>null</jk> if not set.
186    */
187   public Map<String,RequestBodyInfo> getRequestBodies() { return u(requestBodies); }
188
189   /**
190    * Returns the responses map.
191    *
192    * @return The responses map, or <jk>null</jk> if not set.
193    */
194   public Map<String,Response> getResponses() { return u(responses); }
195
196   /**
197    * Returns the schemas map.
198    *
199    * @return The schemas map, or <jk>null</jk> if not set.
200    */
201   public Map<String,SchemaInfo> getSchemas() { return u(schemas); }
202
203   /**
204    * Returns the security schemes map.
205    *
206    * @return The security schemes map, or <jk>null</jk> if not set.
207    */
208   public Map<String,SecuritySchemeInfo> getSecuritySchemes() { return u(securitySchemes); }
209
210   @Override /* Overridden from OpenApiElement */
211   public Set<String> keySet() {
212      // @formatter:off
213      var s = stb(String.class)
214         .addIf(nn(callbacks), "callbacks")
215         .addIf(nn(examples), "examples")
216         .addIf(nn(headers), "headers")
217         .addIf(nn(links), "links")
218         .addIf(nn(parameters), "parameters")
219         .addIf(nn(requestBodies), "requestBodies")
220         .addIf(nn(responses), "responses")
221         .addIf(nn(schemas), "schemas")
222         .addIf(nn(securitySchemes), "securitySchemes")
223         .build();
224      // @formatter:on
225      return new MultiSet<>(s, super.keySet());
226   }
227
228   @Override /* Overridden from OpenApiElement */
229   public Components set(String property, Object value) {
230      reqnn("property", property);
231      return switch (property) {
232         case "callbacks" -> setCallbacks(toMapBuilder(value, String.class, Callback.class).sparse().build());
233         case "examples" -> setExamples(toMapBuilder(value, String.class, Example.class).sparse().build());
234         case "headers" -> setHeaders(toMapBuilder(value, String.class, HeaderInfo.class).sparse().build());
235         case "links" -> setLinks(toMapBuilder(value, String.class, Link.class).sparse().build());
236         case "parameters" -> setParameters(toMapBuilder(value, String.class, Parameter.class).sparse().build());
237         case "requestBodies" -> setRequestBodies(toMapBuilder(value, String.class, RequestBodyInfo.class).sparse().build());
238         case "responses" -> setResponses(toMapBuilder(value, String.class, Response.class).sparse().build());
239         case "schemas" -> setSchemas(toMapBuilder(value, String.class, SchemaInfo.class).sparse().build());
240         case "securitySchemes" -> setSecuritySchemes(toMapBuilder(value, String.class, SecuritySchemeInfo.class).sparse().build());
241         default -> {
242            super.set(property, value);
243            yield this;
244         }
245      };
246   }
247
248   /**
249    * Sets the callbacks map.
250    *
251    * @param value The new value for this property.
252    * @return This object.
253    */
254   public Components setCallbacks(Map<String,Callback> value) {
255      callbacks = value;
256      return this;
257   }
258
259   /**
260    * Sets the examples map.
261    *
262    * @param value The new value for this property.
263    * @return This object.
264    */
265   public Components setExamples(Map<String,Example> value) {
266      examples = value;
267      return this;
268   }
269
270   /**
271    * Sets the headers map.
272    *
273    * @param value The new value for this property.
274    * @return This object.
275    */
276   public Components setHeaders(Map<String,HeaderInfo> value) {
277      headers = value;
278      return this;
279   }
280
281   /**
282    * Sets the links map.
283    *
284    * @param value The new value for this property.
285    * @return This object.
286    */
287   public Components setLinks(Map<String,Link> value) {
288      links = value;
289      return this;
290   }
291
292   /**
293    * Sets the parameters map.
294    *
295    * @param value The new value for this property.
296    * @return This object.
297    */
298   public Components setParameters(Map<String,Parameter> value) {
299      parameters = value;
300      return this;
301   }
302
303   /**
304    * Sets the request bodies map.
305    *
306    * @param value The new value for this property.
307    * @return This object.
308    */
309   public Components setRequestBodies(Map<String,RequestBodyInfo> value) {
310      requestBodies = value;
311      return this;
312   }
313
314   /**
315    * Sets the responses map.
316    *
317    * @param value The new value for this property.
318    * @return This object.
319    */
320   public Components setResponses(Map<String,Response> value) {
321      responses = value;
322      return this;
323   }
324
325   /**
326    * Sets the schemas map.
327    *
328    * @param value The new value for this property.
329    * @return This object.
330    */
331   public Components setSchemas(Map<String,SchemaInfo> value) {
332      schemas = value;
333      return this;
334   }
335
336   /**
337    * Sets the security schemes map.
338    *
339    * @param value The new value for this property.
340    * @return This object.
341    */
342   public Components setSecuritySchemes(Map<String,SecuritySchemeInfo> value) {
343      securitySchemes = value;
344      return this;
345   }
346
347   @Override /* Overridden from OpenApiElement */
348   public Components strict() {
349      super.strict();
350      return this;
351   }
352
353   @Override /* Overridden from OpenApiElement */
354   public Components strict(Object value) {
355      super.strict(value);
356      return this;
357   }
358}