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