001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.oapi;
014
015import java.util.*;
016import java.util.concurrent.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.annotation.*;
020import org.apache.juneau.httppart.*;
021import org.apache.juneau.serializer.*;
022import org.apache.juneau.uon.*;
023
024/**
025 * Serializes POJOs to values suitable for transmission as HTTP headers, query/form-data parameters, and path variables.
026 *
027 * <ul class='seealso'>
028 *    <li class='link'>{@doc juneau-marshall.OpenApiDetails.Serializers}
029 * </ul>
030 */
031@ConfigurableContext
032public class OpenApiSerializer extends UonSerializer implements OpenApiMetaProvider, OpenApiCommon {
033
034   //-------------------------------------------------------------------------------------------------------------------
035   // Configurable properties
036   //-------------------------------------------------------------------------------------------------------------------
037
038   static final String PREFIX = "OpenApiSerializer";
039
040   //-------------------------------------------------------------------------------------------------------------------
041   // Predefined instances
042   //-------------------------------------------------------------------------------------------------------------------
043
044   /** Reusable instance of {@link OpenApiSerializer}, all default settings. */
045   public static final OpenApiSerializer DEFAULT = new OpenApiSerializer(PropertyStore.DEFAULT);
046
047
048   //-------------------------------------------------------------------------------------------------------------------
049   // Instance
050   //-------------------------------------------------------------------------------------------------------------------
051
052   private final Map<ClassMeta<?>,OpenApiClassMeta> openApiClassMetas = new ConcurrentHashMap<>();
053   private final Map<BeanPropertyMeta,OpenApiBeanPropertyMeta> openApiBeanPropertyMetas = new ConcurrentHashMap<>();
054
055   /**
056    * Constructor.
057    *
058    * @param ps
059    *    The property store containing all the settings for this object.
060    * @param produces
061    *    The media type that this serializer produces.
062    * @param accept
063    *    The accept media types that the serializer can handle.
064    *    <p>
065    *    Can contain meta-characters per the <c>media-type</c> specification of {@doc RFC2616.section14.1}
066    *    <p>
067    *    If empty, then assumes the only media type supported is <c>produces</c>.
068    *    <p>
069    *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
070    *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
071    *    <p class='bcode w800'>
072    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>);
073    *    </p>
074    *    <br>...or...
075    *    <p class='bcode w800'>
076    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*&#8203;/json"</js>);
077    *    </p>
078    * <p>
079    * The accept value can also contain q-values.
080    */
081   public OpenApiSerializer(PropertyStore ps, String produces, String accept) {
082      super(
083         ps.builder()
084            .set(UON_encoding, false)
085            .build(),
086         produces,
087         accept
088      );
089   }
090
091   /**
092    * Constructor.
093    *
094    * @param ps
095    *    The property store containing all the settings for this object.
096    */
097   public OpenApiSerializer(PropertyStore ps) {
098      this(ps, "text/openapi", null);
099   }
100
101   @Override /* Context */
102   public OpenApiSerializerBuilder builder() {
103      return new OpenApiSerializerBuilder(getPropertyStore());
104   }
105
106   /**
107    * Instantiates a new clean-slate {@link OpenApiSerializerBuilder} object.
108    *
109    * <p>
110    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
111    * the settings of the object called on.
112    *
113    * @return A new {@link OpenApiSerializerBuilder} object.
114    */
115   public static OpenApiSerializerBuilder create() {
116      return new OpenApiSerializerBuilder();
117   }
118
119   //-----------------------------------------------------------------------------------------------------------------
120   // Entry point methods
121   //-----------------------------------------------------------------------------------------------------------------
122
123   @Override /* Context */
124   public OpenApiSerializerSession createSession() {
125      return createSession(createDefaultSessionArgs());
126   }
127
128   @Override /* Serializer */
129   public OpenApiSerializerSession createSession(SerializerSessionArgs args) {
130      return new OpenApiSerializerSession(this, args);
131   }
132
133   @Override /* HttpPartSerializer */
134   public OpenApiSerializerSession createPartSession() {
135      return createPartSession(null);
136   }
137
138   @Override /* HttpPartSerializer */
139   public OpenApiSerializerSession createPartSession(SerializerSessionArgs args) {
140      return new OpenApiSerializerSession(this, args);
141   }
142
143   @Override /* HttpPartSerializer */
144   public String serialize(HttpPartType partType, HttpPartSchema schema, Object value) throws SchemaValidationException, SerializeException {
145      return createPartSession().serialize(partType, schema, value);
146   }
147
148   @Override /* HttpPartSerializer */
149   public String serialize(HttpPartSchema schema, Object value) throws SchemaValidationException, SerializeException {
150      return createPartSession().serialize(null, schema, value);
151   }
152
153   //-----------------------------------------------------------------------------------------------------------------
154   // Extended metadata
155   //-----------------------------------------------------------------------------------------------------------------
156
157   @Override /* OpenApiMetaProvider */
158   public OpenApiClassMeta getOpenApiClassMeta(ClassMeta<?> cm) {
159      OpenApiClassMeta m = openApiClassMetas.get(cm);
160      if (m == null) {
161         m = new OpenApiClassMeta(cm, this);
162         openApiClassMetas.put(cm, m);
163      }
164      return m;
165   }
166
167   @Override /* OpenApiMetaProvider */
168   public OpenApiBeanPropertyMeta getOpenApiBeanPropertyMeta(BeanPropertyMeta bpm) {
169      if (bpm == null)
170         return OpenApiBeanPropertyMeta.DEFAULT;
171      OpenApiBeanPropertyMeta m = openApiBeanPropertyMetas.get(bpm);
172      if (m == null) {
173         m = new OpenApiBeanPropertyMeta(bpm.getDelegateFor(), this);
174         openApiBeanPropertyMetas.put(bpm, m);
175      }
176      return m;
177   }
178
179   //-----------------------------------------------------------------------------------------------------------------
180   // Other methods
181   //-----------------------------------------------------------------------------------------------------------------
182
183   @Override /* Context */
184   public ObjectMap toMap() {
185      return super.toMap()
186         .append("OpenApiSerializer", new DefaultFilteringObjectMap()
187         );
188   }
189}