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.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021import static org.apache.juneau.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.*;
026import org.apache.juneau.common.utils.*;
027import org.apache.juneau.internal.*;
028
029/**
030 * Used to aid in serialization, deserialization, and validation.
031 *
032 * <p>
033 * The Discriminator Object is used to aid in serialization, deserialization, and validation. It adds support for 
034 * polymorphism by allowing schemas to be discriminated based on the value of a specific property. This is particularly 
035 * useful when working with inheritance hierarchies in object-oriented programming.
036 *
037 * <h5 class='section'>OpenAPI Specification:</h5>
038 * <p>
039 * The Discriminator Object is composed of the following fields:
040 * <ul class='spaced-list'>
041 *    <li><c>propertyName</c> (string, REQUIRED) - The name of the property in the payload that will hold the discriminator value
042 *    <li><c>mapping</c> (map of strings) - An object to hold mappings between payload values and schema names or references
043 * </ul>
044 *
045 * <h5 class='section'>Example:</h5>
046 * <p class='bcode'>
047 *    <jc>// Construct using SwaggerBuilder.</jc>
048 *    Discriminator <jv>x</jv> = <jsm>discriminator</jsm>()
049 *       .setPropertyName(<js>"petType"</js>)
050 *       .setMapping(<jsm>map</jsm>(<js>"dog"</js>, <js>"#/components/schemas/Dog"</js>, <js>"cat"</js>, <js>"#/components/schemas/Cat"</js>));
051 *
052 *    <jc>// Serialize using JsonSerializer.</jc>
053 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
054 *
055 *    <jc>// Or just use toString() which does the same as above.</jc>
056 *    <jv>json</jv> = <jv>x</jv>.toString();
057 * </p>
058 * <p class='bcode'>
059 *    <jc>// Output</jc>
060 *    {
061 *       <js>"propertyName"</js>: <js>"petType"</js>,
062 *       <js>"mapping"</js>: {
063 *          <js>"dog"</js>: <js>"#/components/schemas/Dog"</js>,
064 *          <js>"cat"</js>: <js>"#/components/schemas/Cat"</js>
065 *       }
066 *    }
067 * </p>
068 *
069 * <h5 class='section'>See Also:</h5><ul>
070 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#discriminator-object">OpenAPI Specification &gt; Discriminator Object</a>
071 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/">OpenAPI Inheritance and Polymorphism</a>
072 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
073 * </ul>
074 */
075public class Discriminator extends OpenApiElement {
076
077   private String propertyName;
078   private Map<String,String> mapping;
079
080   /**
081    * Default constructor.
082    */
083   public Discriminator() {}
084
085   /**
086    * Copy constructor.
087    *
088    * @param copyFrom The object to copy.
089    */
090   public Discriminator(Discriminator copyFrom) {
091      super(copyFrom);
092
093      this.propertyName = copyFrom.propertyName;
094      this.mapping = copyOf(copyFrom.mapping);
095   }
096
097   /**
098    * Make a deep copy of this object.
099    *
100    * @return A deep copy of this object.
101    */
102   public Discriminator copy() {
103      return new Discriminator(this);
104   }
105
106   /**
107    * Bean property getter:  <property>propertyName</property>.
108    *
109    * <p>
110    * A short description of the target documentation.
111    *
112    * @return The property value, or <jk>null</jk> if it is not set.
113    */
114   public String getPropertyName() {
115      return propertyName;
116   }
117
118   /**
119    * Bean property setter:  <property>propertyName</property>.
120    *
121    * <p>
122    * A short description of the target documentation.
123    *
124    * @param value
125    *    The new value for this property.
126    *    <br>Can be <jk>null</jk> to unset the property.
127    * @return This object
128    */
129   public Discriminator setPropertyName(String value) {
130      propertyName = value;
131      return this;
132   }
133
134   /**
135    * Bean property getter:  <property>mapping</property>.
136    *
137    * <p>
138    * The URL for the target documentation.
139    *
140    * @return The property value, or <jk>null</jk> if it is not set.
141    */
142   public Map<String,String> getMapping() {
143      return mapping;
144   }
145
146   /**
147    * Bean property setter:  <property>mapping</property>.
148    *
149    * <p>
150    * The URL for the target documentation.
151    *
152    * @param value
153    *    The new value for this property.
154    *    <br>Property value is required.
155    *    <br>URIs defined by {@link UriResolver} can be used for values.
156    *    <br>Can be <jk>null</jk> to unset the property.
157    * @return This object
158    */
159   public Discriminator setMapping(Map<String,String> value) {
160      mapping = copyOf(value);
161      return this;
162   }
163
164   /**
165    * Adds one or more values to the <property>mapping</property> property.
166    *
167    * @param key The key.  Must not be <jk>null</jk>.
168    * @param value The value.  Must not be <jk>null</jk>.
169    * @return This object
170    */
171   public Discriminator addMapping(String key, String value) {
172      assertArgNotNull("key", key);
173      assertArgNotNull("value", value);
174      mapping = mapBuilder(mapping).sparse().add(key, value).build();
175      return this;
176   }
177
178   @Override /* Overridden from OpenApiElement */
179   public <T> T get(String property, Class<T> type) {
180      assertArgNotNull("property", property);
181      return switch (property) {
182         case "propertyName" -> toType(getPropertyName(), type);
183         case "mapping" -> toType(getMapping(), type);
184         default -> super.get(property, type);
185      };
186   }
187
188   @Override /* Overridden from OpenApiElement */
189   public Discriminator set(String property, Object value) {
190      assertArgNotNull("property", property);
191      return switch (property) {
192         case "mapping" -> setMapping(mapBuilder(String.class,String.class).sparse().addAny(value).build());
193         case "propertyName" -> setPropertyName(Utils.s(value));
194         default -> {
195            super.set(property, value);
196            yield this;
197         }
198      };
199   }
200
201   @Override /* Overridden from OpenApiElement */
202   public Set<String> keySet() {
203      var s = setBuilder(String.class)
204         .addIf(mapping != null, "mapping")
205         .addIf(propertyName != null, "propertyName")
206         .build();
207      return new MultiSet<>(s, super.keySet());
208   }
209
210   @Override /* Overridden from OpenApiElement */
211   public Discriminator strict() {
212      super.strict();
213      return this;
214   }
215
216   @Override /* Overridden from OpenApiElement */
217   public Discriminator strict(Object value) {
218      super.strict(value);
219      return this;
220   }
221}