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.marshall.oapi; 018 019import java.util.*; 020import java.util.concurrent.*; 021 022import org.apache.juneau.commons.*; 023import org.apache.juneau.commons.bean.*; 024import org.apache.juneau.commons.collections.*; 025import org.apache.juneau.commons.httppart.*; 026import org.apache.juneau.marshall.*; 027import org.apache.juneau.marshall.uon.*; 028 029/** 030 * OpenAPI part parser. 031 * 032 * <h5 class='section'>Notes:</h5><ul> 033 * <li class='note'>This class is thread safe and reusable. 034 * </ul> 035 * 036 * <h5 class='section'>See Also:</h5><ul> 037 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/OpenApiSupport">OpenApi</a> 038 039 * </ul> 040 */ 041@SuppressWarnings({ 042 "java:S110", // Inheritance depth acceptable for OpenApiParser hierarchy 043 "java:S9149" // Per-format static factories intentionally shadow the parent's. 044}) 045public class OpenApiParser extends UonParser implements OpenApiMetaProvider { 046 047 /** 048 * Builder class. 049 */ 050 public static class Builder extends UonParser.Builder<Builder> { 051 052 private static final Cache<HashKey,OpenApiParser> CACHE = Cache.of(HashKey.class, OpenApiParser.class).build(); 053 054 HttpPartFormat format; 055 HttpPartCollectionFormat collectionFormat; 056 057 /** 058 * Constructor, default settings. 059 */ 060 protected Builder() { 061 consumes("text/openapi"); 062 format = HttpPartFormat.NO_FORMAT; 063 collectionFormat = HttpPartCollectionFormat.NO_COLLECTION_FORMAT; 064 } 065 066 /** 067 * Copy constructor. 068 * 069 * @param copyFrom The builder to copy from. 070 */ 071 protected Builder(Builder copyFrom) { 072 super(copyFrom); 073 format = copyFrom.format; 074 collectionFormat = copyFrom.collectionFormat; 075 } 076 077 /** 078 * Copy constructor. 079 * 080 * @param copyFrom The bean to copy from. 081 */ 082 protected Builder(OpenApiParser copyFrom) { 083 super(copyFrom); 084 format = copyFrom.format; 085 collectionFormat = copyFrom.collectionFormat; 086 } 087 088 @Override /* Overridden from Context.Builder<?> */ 089 public OpenApiParser build() { 090 return cache(CACHE).build(OpenApiParser.class); 091 } 092 093 /** 094 * <i><l>OpenApiCommon</l> configuration property: </i> Default collection format for HTTP parts. 095 * 096 * <p> 097 * Specifies the collection format to use for HTTP parts when not otherwise specified via {@link Schema#collectionFormat()}. 098 * 099 * <h5 class='section'>Example:</h5> 100 * <p class='bjava'> 101 * <jc>// Create a parser using CSV for collections.</jc> 102 * OpenApiParser <jv>parser1</jv> = OpenApiParser 103 * .<jsm>create</jsm>() 104 * .collectionFormat(<jsf>CSV</jsf>) 105 * .build(); 106 * 107 * <jc>// Create a serializer using UON for collections.</jc> 108 * OpenApiParser <jv>parser2</jv> = OpenApiParser 109 * .<jsm>create</jsm>() 110 * .collectionFormat(<jsf>UON</jsf>) 111 * .build(); 112 * 113 * <jc>// Parse CSV.</jc> 114 * JsonList <jv>list1</jv> = <jv>parser1</jv>.read(<js>"foo=bar,baz=qux\,true\,123"</js>, JsonList.<jk>class</jk>) 115 * 116 * <jc>// Parse UON.</jc> 117 * JsonList <jv>list2</jv> = <jv>parser2</jv>.read(<js>"(foo=bar,baz=@(qux,true,123))"</js>, JsonList.<jk>class</jk>) 118 * </p> 119 * 120 * <ul class='values javatree'> 121 * <li class='jc'>{@link HttpPartFormat} 122 * <ul> 123 * <li class='jf'>{@link HttpPartCollectionFormat#CSV CSV} - (default) Comma-separated values (e.g. <js>"foo,bar"</js>). 124 * <li class='jf'>{@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. <js>"foo bar"</js>). 125 * <li class='jf'>{@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. <js>"foo\tbar"</js>). 126 * <li class='jf'>{@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. <js>"foo|bar"</js>). 127 * <li class='jf'>{@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>). 128 * <li class='jf'>{@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. <js>"@(foo,bar)"</js>). 129 * </ul> 130 * </ul> 131 * 132 * @param value The new value for this property. 133 * @return This object. 134 */ 135 public Builder collectionFormat(HttpPartCollectionFormat value) { 136 collectionFormat = value; 137 return this; 138 } 139 140 @Override /* Overridden from Context.Builder<?> */ 141 public Builder copy() { 142 return new Builder(this); 143 } 144 145 /** 146 * <i><l>OpenApiCommon</l> configuration property: </i> Default format for HTTP parts. 147 * 148 * <p> 149 * Specifies the format to use for HTTP parts when not otherwise specified via {@link Schema#format()}. 150 * 151 * <h5 class='section'>Example:</h5> 152 * <p class='bjava'> 153 * <jc>// Create a plain-text parser.</jc> 154 * OpenApiParser <jv>parser1</jv> = OpenApiParser 155 * .<jsm>create</jsm>() 156 * .build(); 157 * 158 * <jc>// Create a UON parser.</jc> 159 * OpenApiParser <jv>parser2</jv> = OpenApiParser 160 * .<jsm>create</jsm>() 161 * .format(<jsf>UON</jsf>) 162 * .build(); 163 * 164 * <jc>// Parse a plain-text string.</jc> 165 * String <jv>value1</jv> = <jv>parser1</jv>.read(<js>"foo bar"</js>); 166 * 167 * <jc>// Parse a UON string.</jc> 168 * String <jv>value2</jv> = <jv>parser1</jv>.read(<js>"'foo bar'"</js>); 169 * </p> 170 * 171 * <ul class='values javatree'> 172 * <li class='jc'>{@link HttpPartFormat} 173 * <ul> 174 * <li class='jf'>{@link HttpPartFormat#UON UON} - UON notation (e.g. <js>"'foo bar'"</js>). 175 * <li class='jf'>{@link HttpPartFormat#INT32 INT32} - Signed 32 bits. 176 * <li class='jf'>{@link HttpPartFormat#INT64 INT64} - Signed 64 bits. 177 * <li class='jf'>{@link HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number. 178 * <li class='jf'>{@link HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number. 179 * <li class='jf'>{@link HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters. 180 * <li class='jf'>{@link HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. <js>"00FF"</js>). 181 * <li class='jf'>{@link HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Spaced-separated hexadecimal encoded octets (e.g. <js>"00 FF"</js>). 182 * <li class='jf'>{@link HttpPartFormat#DATE DATE} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>. 183 * <li class='jf'>{@link HttpPartFormat#DATE_TIME DATE_TIME} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>. 184 * <li class='jf'>{@link HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured. 185 * <li class='jf'>{@link HttpPartFormat#NO_FORMAT NO_FORMAT} - (default) Not specified. 186 * </ul> 187 * </ul> 188 * 189 * @param value The new value for this property. 190 * @return This object. 191 */ 192 public Builder format(HttpPartFormat value) { 193 format = value; 194 return this; 195 } 196 197 @Override /* Overridden from Context.Builder<?> */ 198 public HashKey hashKey() { 199 // @formatter:off 200 return HashKey.of( 201 super.hashKey(), 202 format, 203 collectionFormat 204 ); 205 // @formatter:on 206 } 207 208 209 } 210 211 /** Reusable instance of {@link OpenApiParser}. */ 212 public static final OpenApiParser DEFAULT = new OpenApiParser(create()); 213 214 /** 215 * Creates a new builder for this object. 216 * 217 * @return A new builder. 218 */ 219 public static Builder create() { 220 return new Builder(); 221 } 222 223 final HttpPartFormat format; 224 final HttpPartCollectionFormat collectionFormat; 225 226 private final Map<ClassMeta<?>,OpenApiClassMeta> openApiClassMetas = new ConcurrentHashMap<>(); 227 private final Map<BeanPropertyMeta,OpenApiBeanPropertyMeta> openApiBeanPropertyMetas = new ConcurrentHashMap<>(); 228 229 /** 230 * Constructor. 231 * 232 * @param builder The builder for this object. 233 */ 234 public OpenApiParser(Builder builder) { 235 super(builder); 236 format = builder.format; 237 collectionFormat = builder.collectionFormat; 238 } 239 240 @Override /* Overridden from Context */ 241 public Builder copy() { 242 return new Builder(this); 243 } 244 245 @Override /* Overridden from Context */ 246 public OpenApiParserSession.Builder createSession() { 247 return OpenApiParserSession.create(this); 248 } 249 250 @Override /* Overridden from OpenApiMetaProvider */ 251 public OpenApiBeanPropertyMeta getOpenApiBeanPropertyMeta(BeanPropertyMeta bpm) { 252 if (bpm == null) 253 return OpenApiBeanPropertyMeta.DEFAULT; 254 return openApiBeanPropertyMetas.computeIfAbsent(bpm, k -> new OpenApiBeanPropertyMeta(k.getDelegateFor(), this)); 255 } 256 257 @Override /* Overridden from OpenApiMetaProvider */ 258 public OpenApiClassMeta getOpenApiClassMeta(ClassMeta<?> cm) { 259 return openApiClassMetas.computeIfAbsent(cm, k -> new OpenApiClassMeta(k, this)); 260 } 261 262 @Override /* Overridden from HttpPartParser */ 263 public OpenApiParserSession getPartSession() { return OpenApiParserSession.create(this).build(); } 264 265 @Override /* Overridden from Context */ 266 public OpenApiParserSession getSession() { return createSession().build(); } 267 268 /** 269 * Returns the default collection format to use when not otherwise specified via {@link Schema#collectionFormat()} 270 * 271 * @return The default collection format to use when not otherwise specified via {@link Schema#collectionFormat()} 272 */ 273 protected final HttpPartCollectionFormat getCollectionFormat() { return collectionFormat; } 274 275 /** 276 * Returns the default format to use when not otherwise specified via {@link Schema#format()} 277 * 278 * @return The default format to use when not otherwise specified via {@link Schema#format()} 279 */ 280 protected final HttpPartFormat getFormat() { return format; } 281}