View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.bean.openapi3;
18  
19  import static org.apache.juneau.commons.utils.AssertionUtils.*;
20  import static org.apache.juneau.commons.utils.CollectionUtils.*;
21  import static org.apache.juneau.commons.utils.Utils.*;
22  import static org.apache.juneau.internal.ConverterUtils.*;
23  
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.commons.collections.*;
28  
29  /**
30   * Used to aid in serialization, deserialization, and validation.
31   *
32   * <p>
33   * The Discriminator Object is used to aid in serialization, deserialization, and validation. It adds support for
34   * polymorphism by allowing schemas to be discriminated based on the value of a specific property. This is particularly
35   * useful when working with inheritance hierarchies in object-oriented programming.
36   *
37   * <h5 class='section'>OpenAPI Specification:</h5>
38   * <p>
39   * The Discriminator Object is composed of the following fields:
40   * <ul class='spaced-list'>
41   * 	<li><c>propertyName</c> (string, REQUIRED) - The name of the property in the payload that will hold the discriminator value
42   * 	<li><c>mapping</c> (map of strings) - An object to hold mappings between payload values and schema names or references
43   * </ul>
44   *
45   * <h5 class='section'>Example:</h5>
46   * <p class='bcode'>
47   * 	<jc>// Construct using SwaggerBuilder.</jc>
48   * 	Discriminator <jv>x</jv> = <jsm>discriminator</jsm>()
49   * 		.setPropertyName(<js>"petType"</js>)
50   * 		.setMapping(<jsm>map</jsm>(<js>"dog"</js>, <js>"#/components/schemas/Dog"</js>, <js>"cat"</js>, <js>"#/components/schemas/Cat"</js>));
51   *
52   * 	<jc>// Serialize using JsonSerializer.</jc>
53   * 	String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
54   *
55   * 	<jc>// Or just use toString() which does the same as above.</jc>
56   * 	<jv>json</jv> = <jv>x</jv>.toString();
57   * </p>
58   * <p class='bcode'>
59   * 	<jc>// Output</jc>
60   * 	{
61   * 		<js>"propertyName"</js>: <js>"petType"</js>,
62   * 		<js>"mapping"</js>: {
63   * 			<js>"dog"</js>: <js>"#/components/schemas/Dog"</js>,
64   * 			<js>"cat"</js>: <js>"#/components/schemas/Cat"</js>
65   * 		}
66   * 	}
67   * </p>
68   *
69   * <h5 class='section'>See Also:</h5><ul>
70   * 	<li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#discriminator-object">OpenAPI Specification &gt; Discriminator Object</a>
71   * 	<li class='link'><a class="doclink" href="https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/">OpenAPI Inheritance and Polymorphism</a>
72   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
73   * </ul>
74   */
75  public class Discriminator extends OpenApiElement {
76  
77  	private String propertyName;
78  	private Map<String,String> mapping = map();
79  
80  	/**
81  	 * Default constructor.
82  	 */
83  	public Discriminator() {}
84  
85  	/**
86  	 * Copy constructor.
87  	 *
88  	 * @param copyFrom The object to copy.
89  	 */
90  	public Discriminator(Discriminator copyFrom) {
91  		super(copyFrom);
92  
93  		this.propertyName = copyFrom.propertyName;
94  		this.mapping = copyOf(copyFrom.mapping);
95  	}
96  
97  	/**
98  	 * Adds one or more values to the <property>mapping</property> property.
99  	 *
100 	 * @param key The key.  Must not be <jk>null</jk>.
101 	 * @param value The value.  Must not be <jk>null</jk>.
102 	 * @return This object
103 	 */
104 	public Discriminator addMapping(String key, String value) {
105 		assertArgNotNull("key", key);
106 		assertArgNotNull("value", value);
107 		mapping.put(key, value);
108 		return this;
109 	}
110 
111 	/**
112 	 * Make a deep copy of this object.
113 	 *
114 	 * @return A deep copy of this object.
115 	 */
116 	public Discriminator copy() {
117 		return new Discriminator(this);
118 	}
119 
120 	@Override /* Overridden from OpenApiElement */
121 	public <T> T get(String property, Class<T> type) {
122 		assertArgNotNull("property", property);
123 		return switch (property) {
124 			case "propertyName" -> toType(getPropertyName(), type);
125 			case "mapping" -> toType(getMapping(), type);
126 			default -> super.get(property, type);
127 		};
128 	}
129 
130 	/**
131 	 * Bean property getter:  <property>mapping</property>.
132 	 *
133 	 * <p>
134 	 * The URL for the target documentation.
135 	 *
136 	 * @return The property value, or <jk>null</jk> if it is not set.
137 	 */
138 	public Map<String,String> getMapping() { return nullIfEmpty(mapping); }
139 
140 	/**
141 	 * Bean property getter:  <property>propertyName</property>.
142 	 *
143 	 * <p>
144 	 * A short description of the target documentation.
145 	 *
146 	 * @return The property value, or <jk>null</jk> if it is not set.
147 	 */
148 	public String getPropertyName() { return propertyName; }
149 
150 	@Override /* Overridden from OpenApiElement */
151 	public Set<String> keySet() {
152 		// @formatter:off
153 		var s = setb(String.class)
154 			.addIf(ne(mapping), "mapping")
155 			.addIf(nn(propertyName), "propertyName")
156 			.build();
157 		// @formatter:on
158 		return new MultiSet<>(s, super.keySet());
159 	}
160 
161 	@Override /* Overridden from OpenApiElement */
162 	public Discriminator set(String property, Object value) {
163 		assertArgNotNull("property", property);
164 		return switch (property) {
165 			case "mapping" -> setMapping(toMapBuilder(value, String.class, String.class).sparse().build());
166 			case "propertyName" -> setPropertyName(s(value));
167 			default -> {
168 				super.set(property, value);
169 				yield this;
170 			}
171 		};
172 	}
173 
174 	/**
175 	 * Bean property setter:  <property>mapping</property>.
176 	 *
177 	 * <p>
178 	 * The URL for the target documentation.
179 	 *
180 	 * @param value
181 	 * 	The new value for this property.
182 	 * 	<br>Property value is required.
183 	 * 	<br>URIs defined by {@link UriResolver} can be used for values.
184 	 * 	<br>Can be <jk>null</jk> to unset the property.
185 	 * @return This object
186 	 */
187 	public Discriminator setMapping(Map<String,String> value) {
188 		mapping.clear();
189 		if (nn(value))
190 			mapping.putAll(value);
191 		return this;
192 	}
193 
194 	/**
195 	 * Bean property setter:  <property>propertyName</property>.
196 	 *
197 	 * <p>
198 	 * A short description of the target documentation.
199 	 *
200 	 * @param value
201 	 * 	The new value for this property.
202 	 * 	<br>Can be <jk>null</jk> to unset the property.
203 	 * @return This object
204 	 */
205 	public Discriminator setPropertyName(String value) {
206 		propertyName = value;
207 		return this;
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 }