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 > 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}