You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/06/25 23:21:26 UTC
[juneau] branch master updated: Swagger API improvements.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 854d4c1 Swagger API improvements.
854d4c1 is described below
commit 854d4c13c23011a3333dae35c762d0051a2e0237
Author: JamesBognar <ja...@apache.org>
AuthorDate: Mon Jun 25 19:21:04 2018 -0400
Swagger API improvements.
---
.../main/java/org/apache/juneau/config/Config.java | 2 +-
.../java/org/apache/juneau/config/ConfigMod.java | 7 +-
.../apache/juneau/config/event/ConfigEvent.java | 3 +-
.../apache/juneau/config/internal/ConfigEntry.java | 2 +-
.../juneau/config/store/ConfigFileStore.java | 2 +-
.../apache/juneau/httppart/UonPartParserTest.java | 1 +
.../java/org/apache/juneau/dto/LinkString.java | 1 +
.../org/apache/juneau/dto/swagger/Contact.java | 4 +-
.../juneau/dto/swagger/ExternalDocumentation.java | 2 +-
.../java/org/apache/juneau/dto/swagger/Info.java | 8 +-
.../org/apache/juneau/dto/swagger/License.java | 2 +-
.../org/apache/juneau/dto/swagger/Operation.java | 4 +-
.../src/main/java/org/apache/juneau/BeanMeta.java | 2 +-
.../main/java/org/apache/juneau/BeanSession.java | 2 +
.../main/java/org/apache/juneau/ObjectList.java | 5 +-
.../src/main/java/org/apache/juneau/ObjectMap.java | 53 +-
.../org/apache/juneau/httppart/HttpPartParser.java | 9 +-
.../org/apache/juneau/httppart/HttpPartSchema.java | 1316 ++++++++++++++++++++
.../apache/juneau/httppart/HttpPartSerializer.java | 13 +-
.../org/apache/juneau/httppart/HttpPartType.java | 5 +-
.../apache/juneau/httppart/SimplePartParser.java | 2 +-
.../juneau/httppart/SimplePartSerializer.java | 2 +-
.../juneau/httppart/oapi/OapiPartParser.java | 247 ++++
.../OapiPartParserBuilder.java} | 182 +--
.../OapiPartSerializer.java} | 69 +-
.../OapiPartSerializerBuilder.java} | 226 ++--
.../{ => uon}/SimpleUonPartSerializer.java | 2 +-
.../{ => uon}/SimpleUonPartSerializerBuilder.java | 2 +-
.../juneau/httppart/{ => uon}/UonPartParser.java | 19 +-
.../httppart/{ => uon}/UonPartParserBuilder.java | 2 +-
.../httppart/{ => uon}/UonPartSerializer.java | 16 +-
.../{ => uon}/UonPartSerializerBuilder.java | 2 +-
.../java/org/apache/juneau/internal/DateUtils.java | 4 +-
.../java/org/apache/juneau/internal/IOUtils.java | 3 +-
.../org/apache/juneau/internal/JuneauLogger.java | 2 +-
.../org/apache/juneau/internal/StringUtils.java | 65 +-
.../org/apache/juneau/internal/VersionRange.java | 4 +-
.../org/apache/juneau/json/JsonSerializer.java | 2 +-
.../main/java/org/apache/juneau/utils/AList.java | 13 +
.../org/apache/juneau/utils/CalendarUtils.java | 7 +-
.../java/org/apache/juneau/svl/vars/ArgsVar.java | 2 +-
.../examples/rest/petstore/PetStoreResource.java | 12 +-
.../apache/juneau/microservice/Microservice.java | 2 +-
.../rest/test/client/RequestBeanProxyTest.java | 5 +-
.../rest/test/client/ThirdPartyProxyTest.java | 3 +-
.../apache/juneau/rest/client/NameValuePairs.java | 13 +-
.../org/apache/juneau/rest/client/RestCall.java | 116 +-
.../org/apache/juneau/rest/client/RestClient.java | 23 +-
.../juneau/rest/client/RestClientBuilder.java | 1 +
.../rest/client/SerializedNameValuePair.java | 15 +-
.../apache/juneau/rest/BasicRestInfoProvider.java | 8 +-
.../java/org/apache/juneau/rest/RequestBody.java | 54 +-
.../org/apache/juneau/rest/RequestFormData.java | 163 ++-
.../org/apache/juneau/rest/RequestHeaders.java | 70 +-
.../org/apache/juneau/rest/RequestPathMatch.java | 61 +-
.../java/org/apache/juneau/rest/RequestQuery.java | 107 +-
.../java/org/apache/juneau/rest/RestContext.java | 19 +-
.../org/apache/juneau/rest/RestContextBuilder.java | 11 +-
.../org/apache/juneau/rest/RestParamDefaults.java | 124 +-
.../java/org/apache/juneau/rest/RestRequest.java | 9 +
.../java/org/apache/juneau/rest/RestResponse.java | 2 +-
.../org/apache/juneau/rest/annotation/Body.java | 28 +-
.../org/apache/juneau/rest/annotation/Contact.java | 4 +-
.../juneau/rest/annotation/ExternalDocs.java | 6 +-
.../apache/juneau/rest/annotation/FormData.java | 522 ++++----
.../org/apache/juneau/rest/annotation/Header.java | 500 ++++----
.../org/apache/juneau/rest/annotation/Items.java | 26 +-
.../org/apache/juneau/rest/annotation/License.java | 4 +-
.../juneau/rest/annotation/MethodSwagger.java | 14 +-
.../org/apache/juneau/rest/annotation/Path.java | 381 +++---
.../org/apache/juneau/rest/annotation/Query.java | 525 ++++----
.../juneau/rest/annotation/ResourceSwagger.java | 18 +-
.../apache/juneau/rest/annotation/Response.java | 13 +-
.../juneau/rest/annotation/ResponseHeader.java | 28 +-
.../juneau/rest/annotation/ResponseStatus.java | 3 +-
.../apache/juneau/rest/annotation/RestMethod.java | 3 +-
.../juneau/rest/annotation/RestResource.java | 1 +
.../org/apache/juneau/rest/annotation/Schema.java | 52 +-
.../rest/annotation/{Items.java => SubItems.java} | 27 +-
.../org/apache/juneau/rest/annotation/Tag.java | 10 +-
.../java/org/apache/juneau/rest/mock/MockRest.java | 23 +-
.../juneau/rest/mock/MockServletRequest.java | 4 +-
.../apache/juneau/rest/util/AnnotationUtils.java | 243 ++--
.../org/apache/juneau/rest/util/RestUtils.java | 14 -
.../juneau/rest/BasicRestInfoProviderTest.java | 219 ----
.../juneau/rest/annotation/BodyAnnotationTest.java | 58 +-
.../rest/annotation/FormDataAnnotationTest.java | 392 +-----
.../rest/annotation/HeaderAnnotationTest.java | 340 +----
.../juneau/rest/annotation/PathAnnotationTest.java | 193 +--
.../rest/annotation/QueryAnnotationTest.java | 426 +------
.../annotation/ResponseHeaderAnnotationTest.java | 56 +-
91 files changed, 4205 insertions(+), 3062 deletions(-)
diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
index 9787f6e..b71364b 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
@@ -1370,7 +1370,7 @@ public final class Config extends Context implements ConfigEventListener, Writab
* @return <jk>true</jk> if this section contains the specified key and the key has a non-blank value.
*/
public boolean exists(String key) {
- return ! isEmpty(getString(key, null));
+ return isNotEmpty(getString(key, null));
}
/**
diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
index c592c9d..2da946a 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
@@ -12,9 +12,10 @@
// ***************************************************************************************************************************
package org.apache.juneau.config;
+import static org.apache.juneau.internal.StringUtils.*;
+
import java.util.*;
-import org.apache.juneau.internal.*;
import org.apache.juneau.config.encode.*;
/**
@@ -65,7 +66,7 @@ public enum ConfigMod {
* @return The list of modifiers, or an empty list if the string is empty or <jk>null</jk>.
*/
public static List<ConfigMod> asModifiersReverse(String s) {
- if (StringUtils.isEmpty(s))
+ if (isEmpty(s))
return Collections.emptyList();
if (s.length() == 1) {
ConfigMod m = fromChar(s.charAt(0));
@@ -87,7 +88,7 @@ public enum ConfigMod {
* @return The list of modifiers, or an empty list if the string is empty or <jk>null</jk>.
*/
public static List<ConfigMod> asModifiers(String s) {
- if (StringUtils.isEmpty(s))
+ if (isEmpty(s))
return Collections.emptyList();
if (s.length() == 1) {
ConfigMod m = fromChar(s.charAt(0));
diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/event/ConfigEvent.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/event/ConfigEvent.java
index 1870dfa..2e72079 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/event/ConfigEvent.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/event/ConfigEvent.java
@@ -12,6 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.config.event;
+import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.config.event.ConfigEventType.*;
import java.util.*;
@@ -204,7 +205,7 @@ public class ConfigEvent {
if (val.indexOf('#') != -1)
val = val.replaceAll("#", "\\\\#");
out.append(val);
- if (! StringUtils.isEmpty(comment))
+ if (isNotEmpty(comment))
out.append(" # ").append(comment);
out.append(')');
return out.toString();
diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
index 45c3e06..06090c4 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
@@ -156,7 +156,7 @@ public class ConfigEntry {
}
}
- if (! isEmpty(comment))
+ if (isNotEmpty(comment))
w.append(" # ").append(comment);
w.append('\n');
diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigFileStore.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigFileStore.java
index e87e57c..7ccfe9e 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigFileStore.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/store/ConfigFileStore.java
@@ -255,7 +255,7 @@ public class ConfigFileStore extends ConfigStore {
boolean exists = Files.exists(p);
// Don't create the file if we're not going to match.
- if ((!exists) && (!isEmpty(expectedContents)))
+ if ((!exists) && isNotEmpty(expectedContents))
return "";
if (isWritable(p)) {
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/UonPartParserTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/UonPartParserTest.java
index c5927f7..ba63fac 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/UonPartParserTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/UonPartParserTest.java
@@ -17,6 +17,7 @@ import static org.junit.Assert.*;
import java.util.*;
import org.apache.juneau.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.json.*;
import org.junit.*;
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
index a91d85a..0b38c7b 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
@@ -20,6 +20,7 @@ import org.apache.juneau.annotation.*;
import org.apache.juneau.html.*;
import org.apache.juneau.html.annotation.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.utils.*;
/**
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Contact.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Contact.java
index 23daa40..78abf23 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Contact.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Contact.java
@@ -221,7 +221,7 @@ public class Contact extends SwaggerElement {
* @return <jk>true</jk> if the name property is not null or empty.
*/
public boolean hasName() {
- return ! isEmpty(name);
+ return isNotEmpty(name);
}
/**
@@ -239,7 +239,7 @@ public class Contact extends SwaggerElement {
* @return <jk>true</jk> if the email property is not null or empty.
*/
public boolean hasEmail() {
- return ! isEmpty(email);
+ return isNotEmpty(email);
}
@Override /* SwaggerElement */
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ExternalDocumentation.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ExternalDocumentation.java
index c95e0d9..2546bb1 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ExternalDocumentation.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ExternalDocumentation.java
@@ -190,7 +190,7 @@ public class ExternalDocumentation extends SwaggerElement {
* @return <jk>true</jk> if the description property is not null or empty.
*/
public boolean hasDescription() {
- return ! isEmpty(description);
+ return isNotEmpty(description);
}
@Override /* SwaggerElement */
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Info.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Info.java
index 4868796..ff6ec0b 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Info.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Info.java
@@ -380,7 +380,7 @@ public class Info extends SwaggerElement {
* @return <jk>true</jk> if the title property is not null or empty.
*/
public boolean hasTitle() {
- return ! isEmpty(title);
+ return isNotEmpty(title);
}
/**
@@ -389,7 +389,7 @@ public class Info extends SwaggerElement {
* @return <jk>true</jk> if the description property is not null or empty.
*/
public boolean hasDescription() {
- return ! isEmpty(description);
+ return isNotEmpty(description);
}
/**
@@ -398,7 +398,7 @@ public class Info extends SwaggerElement {
* @return <jk>true</jk> if the version property is not null or empty.
*/
public boolean hasVersion() {
- return ! isEmpty(version);
+ return isNotEmpty(version);
}
/**
@@ -407,7 +407,7 @@ public class Info extends SwaggerElement {
* @return <jk>true</jk> if the termsOfService property is not null or empty.
*/
public boolean hasTermsOfService() {
- return ! isEmpty(termsOfService);
+ return isNotEmpty(termsOfService);
}
@Override /* SwaggerElement */
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/License.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/License.java
index cf35643..7e4f9b6 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/License.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/License.java
@@ -179,7 +179,7 @@ public class License extends SwaggerElement {
* @return <jk>true</jk> if the name property is not null or empty.
*/
public boolean hasName() {
- return ! isEmpty(name);
+ return isNotEmpty(name);
}
/**
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
index a896ee1..6e20de1 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
@@ -1090,7 +1090,7 @@ public class Operation extends SwaggerElement {
* @return <jk>true</jk> if the summary property is not null or empty.
*/
public boolean hasSummary() {
- return ! isEmpty(summary);
+ return isNotEmpty(summary);
}
/**
@@ -1099,7 +1099,7 @@ public class Operation extends SwaggerElement {
* @return <jk>true</jk> if the description property is not null or empty.
*/
public boolean hasDescription() {
- return ! isEmpty(description);
+ return isNotEmpty(description);
}
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index e15aedb..ea19586 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -469,7 +469,7 @@ public class BeanMeta<T> {
private String findPropertyName(Field f, Set<String> fixedBeanProps) {
BeanProperty bp = f.getAnnotation(BeanProperty.class);
String name = bpName(bp);
- if (! isEmpty(name)) {
+ if (isNotEmpty(name)) {
if (fixedBeanProps.isEmpty() || fixedBeanProps.contains(name))
return name;
return null; // Could happen if filtered via BEAN_includeProperties/BEAN_excludeProperties.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 5dc8b02..cdb9125 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -490,6 +490,8 @@ public class BeanSession extends Session {
return (T)new AtomicInteger(Integer.valueOf(n));
if (tc == AtomicLong.class)
return (T)new AtomicLong(Long.valueOf(n));
+ if (tc == Number.class)
+ return (T)StringUtils.parseNumber(n, Number.class);
}
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
index 8e129ae..2550ba8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
@@ -12,11 +12,12 @@
// ***************************************************************************************************************************
package org.apache.juneau;
+import static org.apache.juneau.internal.StringUtils.*;
+
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
-import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.serializer.*;
@@ -275,7 +276,7 @@ public class ObjectList extends LinkedList<Object> {
*/
public ObjectList appendIfNotEmpty(String...o) {
for (String s : o)
- if (! StringUtils.isEmpty(s))
+ if (isNotEmpty(s))
add(s);
return this;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
index cdf2ee0..b37d41c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
@@ -332,10 +332,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
* Convenience method for adding an entry to this map.
*
* <p>
- * Equivalent to calling {@code put(key, value)}, but returns this map so that the method can be chained.
- *
- * <p>
- * <jk>null</jk> and empty string/map/collection values are skipped.
+ * A no-op if the value is <jk>null</jk> or an empty string/map/collection.
*
* @param key The key.
* @param value The value.
@@ -349,6 +346,54 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
* Convenience method for adding an entry to this map.
*
* <p>
+ * A no-op if the value is <jk>false</jk>.
+ *
+ * @param key The key.
+ * @param value The value.
+ * @return This object (for method chaining).
+ */
+ public ObjectMap appendSkipFalse(String key, boolean value) {
+ if (value)
+ append(key, value);
+ return this;
+ }
+
+ /**
+ * Convenience method for adding an entry to this map.
+ *
+ * <p>
+ * A no-op if the value is <code>-1</code>.
+ *
+ * @param key The key.
+ * @param value The value.
+ * @return This object (for method chaining).
+ */
+ public ObjectMap appendSkipMinusOne(String key, long value) {
+ if (value != -1)
+ append(key, value);
+ return this;
+ }
+
+ /**
+ * Convenience method for adding an entry to this map.
+ *
+ * <p>
+ * A no-op if the value is <code>-1</code>.
+ *
+ * @param key The key.
+ * @param value The value.
+ * @return This object (for method chaining).
+ */
+ public ObjectMap appendSkipMinusOne(String key, int value) {
+ if (value != -1)
+ append(key, value);
+ return this;
+ }
+
+ /**
+ * Convenience method for adding an entry to this map.
+ *
+ * <p>
* Equivalent to calling {@code put(key, value)}, but returns this map so that the method can be chained.
*
* <p>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartParser.java
index ce67d94..b3bae5e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartParser.java
@@ -21,7 +21,8 @@ import org.apache.juneau.parser.*;
* <p>
* The following default implementations are provided:
* <ul class='doctree'>
- * <li class='jc'>{@link org.apache.juneau.httppart.UonPartParser} - Parts encoded in UON notation.
+ * <li class='jc'>{@link org.apache.juneau.httppart.oapi.OapiPartParser} - Parts encoded in based on OpenAPI schema.
+ * <li class='jc'>{@link org.apache.juneau.httppart.uon.UonPartParser} - Parts encoded in UON notation.
* <li class='jc'>{@link org.apache.juneau.httppart.SimplePartParser} - Parts encoded in plain text.
* </ul>
*
@@ -43,10 +44,14 @@ public interface HttpPartParser {
* Converts the specified input to the specified class type.
*
* @param partType The part type being parsed.
+ * @param schema
+ * Schema information about the part.
+ * <br>May be <jk>null</jk>.
+ * <br>Not all part parsers use the schema information.
* @param in The input being parsed.
* @param type The category of value being parsed.
* @return The parsed value.
* @throws ParseException
*/
- public <T> T parse(HttpPartType partType, String in, ClassMeta<T> type) throws ParseException;
+ public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
new file mode 100644
index 0000000..5682d11
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
@@ -0,0 +1,1316 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.httppart;
+
+import static java.util.Collections.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.regex.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Represents an OpenAPI schema definition.
+ *
+ * <p>
+ * The schema definition can be applied to any HTTP parts such as bodies, headers, query/form parameters, and URL path parts.
+ * <br>The API is generic enough to apply to any path part although some attributes may only applicable for certain parts.
+ *
+ * <p>
+ * Schema objects are created via builders instantiated through the {@link #create()} method.
+ *
+ * <p>
+ * This class is thread safe and reusable.
+ */
+public class HttpPartSchema {
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Predefined instances
+ //-------------------------------------------------------------------------------------------------------------------
+
+ /** Reusable instance of {@link OapiPartSerializer}, all default settings. */
+ public static final HttpPartSchema DEFAULT = HttpPartSchema.create().build();
+
+ final String _default;
+ final Set<String> _enum;
+ final Map<String,HttpPartSchema> properties;
+ final Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems;
+ final CollectionFormat collectionFormat;
+ final Type type;
+ final Format format;
+ final Pattern pattern;
+ final HttpPartSchema items, additionalProperties;
+ final Number maximum, minimum, multipleOf;
+ final Integer maxItems, maxLength, maxProperties, minItems, minLength, minProperties;
+
+ /**
+ * Instantiates a new builder for this object.
+ *
+ * @return A new builder for this object.
+ */
+ public static Builder create() {
+ return new Builder();
+ }
+
+ HttpPartSchema(Builder b) {
+ this._default = b._default;
+ this._enum = copy(b._enum);
+ this.properties = build(b.properties);
+ this.allowEmptyValue = b.allowEmptyValue;
+ this.exclusiveMaximum = b.exclusiveMaximum;
+ this.exclusiveMinimum = b.exclusiveMinimum;
+ this.required = b.required;
+ this.uniqueItems = b.uniqueItems;
+ this.collectionFormat = b.collectionFormat;
+ this.type = b.type;
+ this.format = b.format;
+ this.pattern = b.pattern;
+ this.items = build(b.items);
+ this.additionalProperties = build(b.additionalProperties);
+ this.maximum = b.maximum;
+ this.minimum = b.minimum;
+ this.multipleOf = b.multipleOf;
+ this.maxItems = b.maxItems;
+ this.maxLength = b.maxLength;
+ this.maxProperties = b.maxProperties;
+ this.minItems = b.minItems;
+ this.minLength = b.minLength;
+ this.minProperties = b.minProperties;
+
+ if (b.noValidate)
+ return;
+
+ // Validation.
+ List<String> errors = new ArrayList<>();
+ AList<String> notAllowed = new AList<>();
+ boolean invalidFormat = false;
+ switch (type) {
+ case STRING: {
+ notAllowed.appendIf(properties != null, "properties");
+ notAllowed.appendIf(additionalProperties != null, "additionalProperties");
+ notAllowed.appendIf(exclusiveMaximum != null, "exclusiveMaximum");
+ notAllowed.appendIf(exclusiveMinimum != null, "exclusiveMinimum");
+ notAllowed.appendIf(uniqueItems != null, "uniqueItems");
+ notAllowed.appendIf(collectionFormat != CollectionFormat.NONE, "collectionFormat");
+ notAllowed.appendIf(items != null, "items");
+ notAllowed.appendIf(maximum != null, "maximum");
+ notAllowed.appendIf(minimum != null, "minimum");
+ notAllowed.appendIf(multipleOf != null, "multipleOf");
+ notAllowed.appendIf(maxItems != null, "maxItems");
+ notAllowed.appendIf(minItems != null, "minItems");
+ notAllowed.appendIf(minProperties != null, "minProperties");
+ invalidFormat = ! format.isOneOf(Format.BYTE, Format.BINARY, Format.DATE, Format.DATE_TIME, Format.PASSWORD, Format.UON, Format.NONE);
+ break;
+ }
+ case ARRAY: {
+ notAllowed.appendIf(properties != null, "properties");
+ notAllowed.appendIf(additionalProperties != null, "additionalProperties");
+ notAllowed.appendIf(exclusiveMaximum != null, "exclusiveMaximum");
+ notAllowed.appendIf(exclusiveMinimum != null, "exclusiveMinimum");
+ notAllowed.appendIf(pattern != null, "pattern");
+ notAllowed.appendIf(maximum != null, "maximum");
+ notAllowed.appendIf(minimum != null, "minimum");
+ notAllowed.appendIf(multipleOf != null, "multipleOf");
+ notAllowed.appendIf(maxLength != null, "maxLength");
+ notAllowed.appendIf(minLength != null, "minLength");
+ notAllowed.appendIf(maxProperties != null, "maxProperties");
+ notAllowed.appendIf(minProperties != null, "minProperties");
+ invalidFormat = ! format.isOneOf(Format.NONE, Format.UON);
+ break;
+ }
+ case BOOLEAN: {
+ notAllowed.appendIf(_enum != null, "_enum");
+ notAllowed.appendIf(properties != null, "properties");
+ notAllowed.appendIf(additionalProperties != null, "additionalProperties");
+ notAllowed.appendIf(exclusiveMaximum != null, "exclusiveMaximum");
+ notAllowed.appendIf(exclusiveMinimum != null, "exclusiveMinimum");
+ notAllowed.appendIf(uniqueItems != null, "uniqueItems");
+ notAllowed.appendIf(collectionFormat != CollectionFormat.NONE, "collectionFormat");
+ notAllowed.appendIf(pattern != null, "pattern");
+ notAllowed.appendIf(items != null, "items");
+ notAllowed.appendIf(maximum != null, "maximum");
+ notAllowed.appendIf(minimum != null, "minimum");
+ notAllowed.appendIf(multipleOf != null, "multipleOf");
+ notAllowed.appendIf(maxItems != null, "maxItems");
+ notAllowed.appendIf(maxLength != null, "maxLength");
+ notAllowed.appendIf(maxProperties != null, "maxProperties");
+ notAllowed.appendIf(minItems != null, "minItems");
+ notAllowed.appendIf(minLength != null, "minLength");
+ notAllowed.appendIf(minProperties != null, "minProperties");
+ invalidFormat = ! format.isOneOf(Format.NONE);
+ break;
+ }
+ case FILE: {
+ break;
+ }
+ case INTEGER: {
+ notAllowed.appendIf(properties != null, "properties");
+ notAllowed.appendIf(additionalProperties != null, "additionalProperties");
+ notAllowed.appendIf(uniqueItems != null, "uniqueItems");
+ notAllowed.appendIf(collectionFormat != CollectionFormat.NONE, "collectionFormat");
+ notAllowed.appendIf(pattern != null, "pattern");
+ notAllowed.appendIf(items != null, "items");
+ notAllowed.appendIf(maxItems != null, "maxItems");
+ notAllowed.appendIf(maxLength != null, "maxLength");
+ notAllowed.appendIf(maxProperties != null, "maxProperties");
+ notAllowed.appendIf(minItems != null, "minItems");
+ notAllowed.appendIf(minLength != null, "minLength");
+ notAllowed.appendIf(minProperties != null, "minProperties");
+ invalidFormat = ! format.isOneOf(Format.NONE, Format.INT32, Format.INT64);
+ break;
+ }
+ case NUMBER: {
+ notAllowed.appendIf(properties != null, "properties");
+ notAllowed.appendIf(additionalProperties != null, "additionalProperties");
+ notAllowed.appendIf(uniqueItems != null, "uniqueItems");
+ notAllowed.appendIf(collectionFormat != CollectionFormat.NONE, "collectionFormat");
+ notAllowed.appendIf(pattern != null, "pattern");
+ notAllowed.appendIf(items != null, "items");
+ notAllowed.appendIf(maxItems != null, "maxItems");
+ notAllowed.appendIf(maxLength != null, "maxLength");
+ notAllowed.appendIf(maxProperties != null, "maxProperties");
+ notAllowed.appendIf(minItems != null, "minItems");
+ notAllowed.appendIf(minLength != null, "minLength");
+ notAllowed.appendIf(minProperties != null, "minProperties");
+ invalidFormat = ! format.isOneOf(Format.NONE, Format.FLOAT, Format.DOUBLE);
+ break;
+ }
+ case OBJECT: {
+ notAllowed.appendIf(exclusiveMaximum != null, "exclusiveMaximum");
+ notAllowed.appendIf(exclusiveMinimum != null, "exclusiveMinimum");
+ notAllowed.appendIf(uniqueItems != null, "uniqueItems");
+ notAllowed.appendIf(collectionFormat != CollectionFormat.NONE, "collectionFormat");
+ notAllowed.appendIf(pattern != null, "pattern");
+ notAllowed.appendIf(items != null, "items");
+ notAllowed.appendIf(maximum != null, "maximum");
+ notAllowed.appendIf(minimum != null, "minimum");
+ notAllowed.appendIf(multipleOf != null, "multipleOf");
+ notAllowed.appendIf(maxItems != null, "maxItems");
+ notAllowed.appendIf(maxLength != null, "maxLength");
+ notAllowed.appendIf(minItems != null, "minItems");
+ notAllowed.appendIf(minLength != null, "minLength");
+ invalidFormat = ! format.isOneOf(Format.NONE, Format.UON);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (! notAllowed.isEmpty())
+ errors.add("Attributes not allow for type='"+type+"': " + StringUtils.join(notAllowed, ","));
+ if (invalidFormat)
+ errors.add("Invalid format for type='"+type+"': '"+format+"'");
+ if (exclusiveMaximum != null && maximum == null)
+ errors.add("Cannot specify exclusiveMaximum with maximum.");
+ if (exclusiveMinimum != null && minimum == null)
+ errors.add("Cannot specify exclusiveMinimum with minimum.");
+ if (required != null && required && _default != null)
+ errors.add("Cannot specify a default value on a required value.");
+
+ if (! errors.isEmpty())
+ throw new ContextRuntimeException("Schema specification errors: \n\t" + join(errors, "\n\t"));
+ }
+
+ /**
+ * The builder class for creating {@link HttpPartSchema} objects.
+ *
+ */
+ public static class Builder {
+ String _default;
+ Set<String> _enum;
+ Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems;
+ CollectionFormat collectionFormat = CollectionFormat.NONE;
+ Type type = Type.NONE;
+ Format format = Format.NONE;
+ Pattern pattern;
+ Number maximum, minimum, multipleOf;
+ Integer maxItems, maxLength, maxProperties, minItems, minLength, minProperties;
+ Map<String,Builder> properties;
+ HttpPartSchema.Builder items, additionalProperties;
+ boolean noValidate;
+
+ /**
+ * Instantiates a new {@link HttpPartSchema} object based on the configuration of this builder.
+ *
+ * <p>
+ * This method can be called multiple times to produce new schema objects.
+ *
+ * @return
+ * A new {@link HttpPartSchema} object.
+ * <br>Never <jk>null</jk>.
+ */
+ public HttpPartSchema build() {
+ return new HttpPartSchema(this);
+ }
+
+ /**
+ * <mk>required</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Determines whether the parameter is mandatory.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder required(Boolean value) {
+ if (value != null)
+ this.required = value;
+ return this;
+ }
+
+ /**
+ * <mk>type</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * The type of the parameter.
+ *
+ * <p>
+ * The possible values are:
+ * <ul class='spaced-list'>
+ * <li>
+ * <js>"string"</js>
+ * <br>Parameter must be a string or a POJO convertible from a string.
+ * <li>
+ * <js>"number"</js>
+ * <br>Parameter must be a number primitive or number object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Float</code> or <code>Double</code> depending on the size of the number.
+ * <li>
+ * <js>"integer"</js>
+ * <br>Parameter must be a integer/long primitive or integer/long object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Short</code>, <code>Integer</code>, or <code>Long</code> depending on the size of the number.
+ * <li>
+ * <js>"boolean"</js>
+ * <br>Parameter must be a boolean primitive or object.
+ * <li>
+ * <js>"array"</js>
+ * <br>Parameter must be an array or collection.
+ * <br>Elements must be strings or POJOs convertible from strings.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectList}.
+ * <li>
+ * <js>"object"</js>
+ * <br>Parameter must be a map or bean.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectMap}.
+ * <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
+ * <li>
+ * <js>"file"</js>
+ * <br>This type is currently not supported.
+ * </ul>
+ *
+ * <p>
+ * If the type is not specified, it will be auto-detected based on the parameter class type.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/#dataTypes'>Swagger specification > Data Types</a>
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder type(String value) {
+ try {
+ if (isNotEmpty(value))
+ this.type = Type.fromString(value);
+ } catch (Exception e) {
+ throw new ContextRuntimeException("Invalid value ''{0}'' passed in as type value. Valid values: {1}", value, Type.values());
+ }
+ return this;
+ }
+
+ /**
+ * <mk>format</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * The extending format for the previously mentioned <a href='https://swagger.io/specification/v2/#parameterType'>type</a>.
+ *
+ * <p>
+ * The possible values are:
+ * <ul class='spaced-list'>
+ * <li>
+ * <js>"int32"</js> - Signed 32 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"int64"</js> - Signed 64 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"float"</js> - 32-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"double"</js> - 64-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"byte"</js> - BASE-64 encoded characters.
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"password"</js> - Used to hint UIs the input needs to be obscured.
+ * <br>This format does not affect the serialization or parsing of the parameter.
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ * <br>Only valid with type <js>"object"</js>.
+ * <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/v2/#dataTypeFormat'>Swagger specification > Data Type Formats</a>
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder format(String value) {
+ try {
+ if (isNotEmpty(value))
+ this.format = Format.fromString(value);
+ } catch (Exception e) {
+ throw new ContextRuntimeException("Invalid value ''{0}'' passed in as format value. Valid values: {1}", value, Format.values());
+ }
+ return this;
+ }
+
+ /**
+ * <mk>allowEmptyValue</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Sets the ability to pass empty-valued parameters.
+ * <br>This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value.
+ * <br>The default value is <jk>false</jk>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder allowEmptyValue(Boolean value) {
+ if (value != null)
+ this.allowEmptyValue = value;
+ return this;
+ }
+
+ /**
+ * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Describes the type of items in the array.
+ * <p>
+ * Required if <code>type</code> is <js>"array"</js>.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder items(Builder value) {
+ if (value != null)
+ items = value;
+ return this;
+ }
+
+ /**
+ * <mk>collectionFormat</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Determines the format of the array if <code>type</code> <js>"array"</js> is used.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
+ *
+ * <br>Possible values are:
+ * <ul class='spaced-list'>
+ * <li>
+ * <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
+ * <li>
+ * <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
+ * <li>
+ * <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ * <li>
+ * <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ * <li>
+ * <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
+ * <li>
+ * </ul>
+ *
+ * <p>
+ * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder collectionFormat(String value) {
+ try {
+ if (isNotEmpty(value))
+ this.collectionFormat = CollectionFormat.fromString(value);
+ } catch (Exception e) {
+ throw new ContextRuntimeException("Invalid value ''{0}'' passed in as collectionFormat value. Valid values: {1}", value, CollectionFormat.values());
+ }
+ return this;
+ }
+
+ /**
+ * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request.
+ * <br>(Note: "default" has no meaning for required parameters.)
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder _default(String value) {
+ if (isNotEmpty(value))
+ this._default = value;
+ return this;
+ }
+
+ /**
+ * <mk>maximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines the maximum value for a parameter of numeric types.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder maximum(Number value) {
+ if (value != null)
+ this.maximum = value;
+ return this;
+ }
+
+ /**
+ * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines whether the maximum is matched exclusively.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>maximum</code>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder exclusiveMaximum(Boolean value) {
+ if (value != null)
+ this.exclusiveMaximum = value;
+ return this;
+ }
+
+ /**
+ * <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines the minimum value for a parameter of numeric types.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder minimum(Number value) {
+ if (value != null)
+ this.minimum = value;
+ return this;
+ }
+
+ /**
+ * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines whether the minimum is matched exclusively.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>minimum</code>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder exclusiveMinimum(Boolean value) {
+ if (value != null)
+ this.exclusiveMinimum = value;
+ return this;
+ }
+
+ /**
+ * <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder maxLength(Integer value) {
+ if (value != null)
+ this.maxLength = value;
+ return this;
+ }
+
+ /**
+ * <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder minLength(Integer value) {
+ if (value != null)
+ this.minLength = value;
+ return this;
+ }
+
+ /**
+ * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * A string input is valid if it matches the specified regular expression pattern.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder pattern(String value) {
+ try {
+ if (isNotEmpty(value))
+ this.pattern = Pattern.compile(value);
+ } catch (Exception e) {
+ throw new ContextRuntimeException(e, "Invalid value {0} passed in as pattern value. Must be a valid regular expression.", value);
+ }
+ return this;
+ }
+
+ /**
+ * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * An array or collection is valid if its size is less than, or equal to, the value of this keyword.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder maxItems(Integer value) {
+ if (value != null)
+ this.maxItems = value;
+ return this;
+ }
+
+ /**
+ * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * An array or collection is valid if its size is greater than, or equal to, the value of this keyword.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder minItems(Integer value) {
+ if (value != null)
+ this.minItems = value;
+ return this;
+ }
+
+ /**
+ * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * If <jk>true</jk>, the input validates successfully if all of its elements are unique.
+ *
+ * <p>
+ * <br>If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway).
+ * <br>Otherwise, the collection or array is checked for duplicate items.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder uniqueItems(Boolean value) {
+ if (value != null)
+ this.uniqueItems = value;
+ return this;
+ }
+
+ /**
+ * <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * If specified, the input validates successfully if it is equal to one of the elements in this array.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder _enum(Set<String> value) {
+ if (value != null)
+ this._enum = value;
+ return this;
+ }
+
+ /**
+ * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder multipleOf(Number value) {
+ if (value != null)
+ this.multipleOf = value;
+ return this;
+ }
+
+ /**
+ * TODO
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder maxProperties(Integer value) {
+ if (value != null)
+ this.maxProperties = value;
+ return this;
+ }
+
+ /**
+ * TODO
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder minProperties(Integer value) {
+ if (value != null)
+ this.minProperties = value;
+ return this;
+ }
+
+ /**
+ * TODO
+ *
+ * @param name The property name.
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder property(String name, Builder value) {
+ if (isNotEmpty(name) && isNotEmpty(value))
+ properties.put(name, value);
+ return this;
+ }
+
+ /**
+ * TODO
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder additionalProperties(Builder value) {
+ if (value != null)
+ additionalProperties = value;
+ return additionalProperties;
+ }
+
+ /**
+ * TODO
+ *
+ * @param value
+ * The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder noValidate(boolean value) {
+ this.noValidate = value;
+ return this;
+ }
+ }
+
+ /**
+ * Valid values for the <code>collectionFormat</code> field.
+ */
+ public static enum CollectionFormat {
+
+ /**
+ * Comma-separated values (e.g. <js>"foo,bar"</js>).
+ */
+ CSV,
+
+ /**
+ * Space-separated values (e.g. <js>"foo bar"</js>).
+ */
+ SSV,
+
+ /**
+ * Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ */
+ TSV,
+
+ /**
+ * Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ */
+ PIPES,
+
+ /**
+ * Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ */
+ MULTI,
+
+ /**
+ * UON notation (e.g. <js>"@(foo,bar)"</js>).
+ */
+ UON,
+
+ /**
+ * Not specified.
+ */
+ NONE;
+
+ static CollectionFormat fromString(String value) {
+
+ return valueOf(value.toUpperCase());
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+ }
+
+ /**
+ * Valid values for the <code>type</code> field.
+ */
+ public static enum Type {
+
+ /**
+ * String.
+ */
+ STRING,
+
+ /**
+ * Floating point number.
+ */
+ NUMBER,
+
+ /**
+ * Decimal number.
+ */
+ INTEGER,
+
+ /**
+ * Boolean.
+ */
+ BOOLEAN,
+
+ /**
+ * Array or collection.
+ */
+ ARRAY,
+
+ /**
+ * Map or bean.
+ */
+ OBJECT,
+
+ /**
+ * File.
+ */
+ FILE,
+
+ /**
+ * Not specified.
+ */
+ NONE;
+
+ static Type fromString(String value) {
+ return valueOf(value.toUpperCase());
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+ }
+
+ /**
+ * Valid values for the <code>format</code> field.
+ */
+ public static enum Format {
+
+ /**
+ * Signed 32 bits.
+ */
+ INT32,
+
+ /**
+ * Signed 64 bits.
+ */
+ INT64,
+
+ /**
+ * 32-bit floating point number.
+ */
+ FLOAT,
+
+ /**
+ * 64-bit floating point number.
+ */
+ DOUBLE,
+
+ /**
+ * BASE-64 encoded characters.
+ */
+ BYTE,
+
+ /**
+ * Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ */
+ BINARY,
+
+ /**
+ * An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ */
+ DATE,
+
+ /**
+ * An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ */
+ DATE_TIME,
+
+ /**
+ * Used to hint UIs the input needs to be obscured.
+ */
+ PASSWORD,
+
+ /**
+ * UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ */
+ UON,
+
+ /**
+ * Not specified.
+ */
+ NONE;
+
+ static Format fromString(String value) {
+ value = value.toUpperCase().replace('-','_');
+ return valueOf(value);
+ }
+
+ @Override
+ public String toString() {
+ String s = name().toLowerCase().replace('_','-');
+ return s;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this format is in the provided list.
+ *
+ * @param list The list of formats to check against.
+ * @return <jk>true</jk> if this format is in the provided list.
+ */
+ public boolean isOneOf(Format...list) {
+ for (Format ff : list)
+ if (this == ff)
+ return true;
+ return false;
+ }
+ }
+
+ /**
+ * Returns the default value for this schema.
+ *
+ * @return The default value for this schema, or <jk>null</jk> if not specified.
+ */
+ public String getDefault() {
+ return _default;
+ }
+
+ /**
+ * Returns the <code>collectionFormat</code> field of this schema.
+ *
+ * @return The <code>collectionFormat</code> field of this schema.
+ */
+ public CollectionFormat getCollectionFormat() {
+ return collectionFormat;
+ }
+
+ /**
+ * Returns the type field of this schema.
+ *
+ * @param cm
+ * The class meta of the object.
+ * <br>Used to auto-detect the type if the type was not specified.
+ * @return The format field of this schema.
+ */
+ public Type getType(ClassMeta<?> cm) {
+ if (type != Type.NONE)
+ return type;
+ if (cm.isMapOrBean())
+ return Type.OBJECT;
+ if (cm.isCollectionOrArray())
+ return Type.ARRAY;
+ if (cm.isInteger())
+ return Type.INTEGER;
+ if (cm.isNumber())
+ return Type.NUMBER;
+ if (cm.isBoolean())
+ return Type.BOOLEAN;
+ return Type.STRING;
+ }
+
+ /**
+ * Returns the <code>format</code> field of this schema.
+ *
+ * @return The <code>format</code> field of this schema.
+ */
+ public Format getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns the schema for child items of the object represented by this schema.
+ *
+ * @return The schema for child items of the object represented by this schema, or <jk>null</jk> if not defined.
+ */
+ public HttpPartSchema getItems() {
+ return items;
+ }
+
+ /**
+ * Throws a {@link ParseException} if the specified pre-parsed input does not validate against this schema.
+ *
+ * @param in The input.
+ * @return The same object passed in.
+ * @throws ParseException if the specified pre-parsed input does not validate against this schema.
+ */
+ public String validateInput(String in) throws ParseException {
+ if (! isValidRequired(in))
+ throw new ParseException("No value specified.");
+ if (in != null) {
+ if (! isValidAllowEmpty(in))
+ throw new ParseException("Empty value not allowed.");
+ if (! isValidPattern(in))
+ throw new ParseException("Value does not match expected pattern. Must match pattern: {0}", pattern.pattern());
+ if (! isValidEnum(in))
+ throw new ParseException("Value does not match one of the expected values. Must be one of the following: {0}", _enum);
+ if (! isValidMaxLength(in))
+ throw new ParseException("Maximum length of value exceeded.");
+ if (! isValidMinLength(in))
+ throw new ParseException("Minimum length of value exceeded.");
+ }
+ return in;
+ }
+
+ /**
+ * Throws a {@link ParseException} if the specified parsed output does not validate against this schema.
+ *
+ * @param o The parsed output.
+ * @param bc The bean context used to detect POJO types.
+ * @return The same object passed in.
+ * @throws ParseException if the specified parsed output does not validate against this schema.
+ */
+ @SuppressWarnings("rawtypes")
+ public Object validateOutput(Object o, BeanContext bc) throws ParseException {
+ if (o == null) {
+ if (! isValidRequired(o))
+ throw new ParseException("Required value not provided.");
+ return o;
+ }
+ ClassMeta<?> cm = bc.getClassMetaForObject(o);
+ switch (getType(cm)) {
+ case STRING: {
+ if (cm.isString())
+ return validateInput(o.toString());
+ break;
+ }
+ case ARRAY: {
+ if (cm.isArray()) {
+ if (! isValidMinItems(o))
+ throw new ParseException("Minimum items of value exceeded.");
+ if (! isValidMaxItems(o))
+ throw new ParseException("Maximum items of value exceeded.");
+ if (! isValidUniqueItems(o))
+ throw new ParseException("Duplicate items found.");
+ HttpPartSchema items = getItems();
+ if (items != null)
+ for (int i = 0; i < Array.getLength(o); i++)
+ items.validateOutput(Array.get(o, i), bc);
+ } else if (cm.isCollection()) {
+ Collection<?> c = (Collection<?>)o;
+ if (! isValidMinItems(c))
+ throw new ParseException("Minimum items of value exceeded.");
+ if (! isValidMaxItems(c))
+ throw new ParseException("Maximum items of value exceeded.");
+ if (! isValidUniqueItems(c))
+ throw new ParseException("Duplicate items found.");
+ HttpPartSchema items = getItems();
+ if (items != null)
+ for (Object o2 : c)
+ items.validateOutput(o2, bc);
+ }
+ break;
+ }
+ case INTEGER: {
+ if (cm.isNumber()) {
+ Number n = (Number)o;
+ if (! isValidMinimum(n))
+ throw new ParseException("Minimal value exceeded.");
+ if (! isValidMaximum(n))
+ throw new ParseException("Maximum value exceeded.");
+ if (! isValidMultipleOf(n))
+ throw new ParseException("Multiple-of not met.");
+ }
+ break;
+ }
+ case NUMBER: {
+ if (cm.isNumber()) {
+ Number n = (Number)o;
+ if (! isValidMinimum(n))
+ throw new ParseException("Minimal value exceeded.");
+ if (! isValidMaximum(n))
+ throw new ParseException("Maximum value exceeded.");
+ if (! isValidMultipleOf(n))
+ throw new ParseException("Multiple-of not met.");
+ }
+ break;
+ }
+ case OBJECT: {
+ if (cm.isMapOrBean()) {
+ Map<?,?> m = cm.isMap() ? (Map<?,?>)o : bc.createSession().toBeanMap(o);
+ if (! isValidMinProperties(m))
+ throw new ParseException("Minimum number properties of value not met.");
+ if (! isValidMaxProperties(m))
+ throw new ParseException("Maximum number of properties of value exceeded.");
+ for (Map.Entry e : m.entrySet()) {
+ String key = e.getKey().toString();
+ HttpPartSchema s2 = getProperty(key);
+ if (s2 != null)
+ s2.validateOutput(e.getValue(), bc);
+ }
+ } else if (cm.isBean()) {
+
+ }
+ break;
+ }
+ case BOOLEAN:
+ case FILE:
+ case NONE:
+ break;
+ }
+ return o;
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods.
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private boolean isValidRequired(Object x) {
+ return x != null || required == null || ! required;
+ }
+
+ private boolean isValidMinProperties(Map<?,?> x) {
+ return minProperties == null || x.size() >= minProperties;
+ }
+
+ private boolean isValidMaxProperties(Map<?,?> x) {
+ return maxProperties == null || x.size() <= maxProperties;
+ }
+
+ private boolean isValidMinimum(Number x) {
+ if (x instanceof Integer)
+ return minimum == null || x.intValue() > minimum.intValue() || (x.intValue() == minimum.intValue() && (exclusiveMinimum == null || ! exclusiveMinimum));
+ if (x instanceof Short)
+ return minimum == null || x.shortValue() > minimum.shortValue() || (x.intValue() == minimum.shortValue() && (exclusiveMinimum == null || ! exclusiveMinimum));
+ if (x instanceof Long)
+ return minimum == null || x.longValue() > minimum.longValue() || (x.intValue() == minimum.longValue() && (exclusiveMinimum == null || ! exclusiveMinimum));
+ if (x instanceof Float)
+ return minimum == null || x.floatValue() > minimum.floatValue() || (x.floatValue() == minimum.floatValue() && (exclusiveMinimum == null || ! exclusiveMinimum));
+ if (x instanceof Double)
+ return minimum == null || x.doubleValue() > minimum.doubleValue() || (x.doubleValue() == minimum.doubleValue() && (exclusiveMinimum == null || ! exclusiveMinimum));
+ return true;
+ }
+
+ private boolean isValidMaximum(Number x) {
+ if (x instanceof Integer)
+ return maximum == null || x.intValue() < maximum.intValue() || (x.intValue() == maximum.intValue() && (exclusiveMaximum == null || ! exclusiveMaximum));
+ if (x instanceof Short)
+ return maximum == null || x.shortValue() < maximum.shortValue() || (x.intValue() == maximum.shortValue() && (exclusiveMaximum == null || ! exclusiveMaximum));
+ if (x instanceof Long)
+ return maximum == null || x.longValue() < maximum.longValue() || (x.intValue() == maximum.longValue() && (exclusiveMaximum == null || ! exclusiveMaximum));
+ if (x instanceof Float)
+ return maximum == null || x.floatValue() < maximum.floatValue() || (x.floatValue() == maximum.floatValue() && (exclusiveMaximum == null || ! exclusiveMaximum));
+ if (x instanceof Double)
+ return maximum == null || x.doubleValue() < maximum.doubleValue() || (x.doubleValue() == maximum.doubleValue() && (exclusiveMaximum == null || ! exclusiveMaximum));
+ return true;
+ }
+
+ private boolean isValidMultipleOf(Number x) {
+ if (x instanceof Integer)
+ return multipleOf == null || x.intValue() % multipleOf.intValue() == 0;
+ if (x instanceof Short)
+ return multipleOf == null || x.shortValue() % multipleOf.shortValue() == 0;
+ if (x instanceof Long)
+ return multipleOf == null || x.longValue() % multipleOf.longValue() == 0;
+ if (x instanceof Float)
+ return multipleOf == null || x.floatValue() % multipleOf.floatValue() == 0;
+ if (x instanceof Double)
+ return multipleOf == null || x.doubleValue() % multipleOf.doubleValue() == 0;
+ return true;
+ }
+
+ private boolean isValidAllowEmpty(String x) {
+ return allowEmptyValue == null || allowEmptyValue || isNotEmpty(x);
+ }
+
+ private boolean isValidPattern(String x) {
+ return pattern == null || pattern.matcher(x).matches();
+ }
+
+ private boolean isValidEnum(String x) {
+ return _enum == null || _enum.contains(x);
+ }
+
+ private boolean isValidMinLength(String x) {
+ return minLength == null || x.length() >= minLength;
+ }
+
+ private boolean isValidMaxLength(String x) {
+ return maxLength == null || x.length() <= maxLength;
+ }
+
+ private boolean isValidMinItems(Object x) {
+ return minItems == null || Array.getLength(x) >= minItems;
+ }
+
+ private boolean isValidMaxItems(Object x) {
+ return maxItems == null || Array.getLength(x) <= maxItems;
+ }
+
+ private boolean isValidUniqueItems(Object x) {
+ if (uniqueItems != null && uniqueItems) {
+ Set<Object> s = new HashSet<>();
+ for (int i = 0; i < Array.getLength(x); i++) {
+ Object o = Array.get(x, i);
+ if (! s.add(o))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isValidMinItems(Collection<?> x) {
+ return minItems == null || x.size() >= minItems;
+ }
+
+ private boolean isValidMaxItems(Collection<?> x) {
+ return maxItems == null || x.size() <= maxItems;
+ }
+
+ private boolean isValidUniqueItems(Collection<?> x) {
+ if (uniqueItems != null && uniqueItems && ! (x instanceof Set)) {
+ Set<Object> s = new HashSet<>();
+ for (Object o : x)
+ if (! s.add(o))
+ return false;
+ }
+ return true;
+ }
+
+ private HttpPartSchema getProperty(String name) {
+ if (properties != null) {
+ HttpPartSchema schema = properties.get(name);
+ if (schema != null)
+ return schema;
+ }
+ return additionalProperties;
+ }
+
+
+ private static Set<String> copy(Set<String> in) {
+ return in == null ? null : unmodifiableSet(new LinkedHashSet<>(in));
+ }
+
+ private static Map<String,HttpPartSchema> build(Map<String,Builder> in) {
+ if (in == null)
+ return null;
+ Map<String,HttpPartSchema> m = new LinkedHashMap<>();
+ for (Map.Entry<String,Builder> e : in.entrySet())
+ m.put(e.getKey(), e.getValue().build());
+ return unmodifiableMap(m);
+ }
+
+ private static HttpPartSchema build(Builder in) {
+ return in == null ? null : in.build();
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
index 93e256e..ac6bbf7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
@@ -22,8 +22,9 @@ import org.apache.juneau.remoteable.*;
* <p>
* The following default implementations are provided:
* <ul class='doctree'>
- * <li class='jc'>{@link org.apache.juneau.httppart.UonPartSerializer} - Parts encoded in UON notation.
- * <li class='jc'>{@link org.apache.juneau.httppart.SimpleUonPartSerializer} - Parts encoded in UON notation, but
+ * <li class='jc'>{@link org.apache.juneau.httppart.oapi.OapiPartSerializer} - Parts encoded based on OpenAPI schema.
+ * <li class='jc'>{@link org.apache.juneau.httppart.uon.UonPartSerializer} - Parts encoded in UON notation.
+ * <li class='jc'>{@link org.apache.juneau.httppart.uon.SimpleUonPartSerializer} - Parts encoded in UON notation, but
* strings are treated as plain-text and arrays/collections are serialized as comma-delimited lists.
* <li class='jc'>{@link org.apache.juneau.httppart.SimplePartSerializer} - Parts encoded in plain text.
* </ul>
@@ -64,8 +65,12 @@ public interface HttpPartSerializer {
* Returned values should NOT be URL-encoded.
*
* @param type The category of value being serialized.
- * @param value The value being serialized.
+ * @param schema
+ * Schema information about the part.
+ * <br>May be <jk>null</jk>.
+ * <br>Not all part serializer use the schema information.
+ * @param value The value being serialized.
* @return The serialized value.
*/
- public String serialize(HttpPartType type, Object value);
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
index 5f3df96..1230f44 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
@@ -13,10 +13,13 @@
package org.apache.juneau.httppart;
/**
- * Represents possible enum values that can be passed to the {@link HttpPartSerializer#serialize(HttpPartType, Object)}.
+ * Represents possible enum values that can be passed to the {@link HttpPartSerializer#serialize(HttpPartType, HttpPartSchema, Object)}.
*/
public enum HttpPartType {
+ /** An HTTP request body */
+ BODY,
+
/** A URI path variable */
PATH,
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartParser.java
index c7be584..842ae54 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartParser.java
@@ -55,7 +55,7 @@ public class SimplePartParser implements HttpPartParser {
//-------------------------------------------------------------------------------------------------------------------
@Override
- public <T> T parse(HttpPartType partType, String in, ClassMeta<T> type) throws ParseException {
+ public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException {
return ClassUtils.fromString(type.getInnerClass(), in);
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartSerializer.java
index 31f3316..c10f1c9 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimplePartSerializer.java
@@ -35,7 +35,7 @@ public class SimplePartSerializer implements HttpPartSerializer {
//-------------------------------------------------------------------------------------------------------------------
@Override /* PartSerializer */
- public String serialize(HttpPartType type, Object value) {
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
return ClassUtils.toString(value);
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParser.java
new file mode 100644
index 0000000..d76e1be
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParser.java
@@ -0,0 +1,247 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.httppart.oapi;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * OpenAPI part parser.
+ */
+public class OapiPartParser extends UonPartParser {
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Configurable properties
+ //-------------------------------------------------------------------------------------------------------------------
+
+ private static final String PREFIX = "OapiPartParser.";
+
+ /**
+ * Configuration property: OpenAPI schema description.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"OapiPartParser.schema"</js>
+ * <li><b>Data type:</b> <code>HttpPartSchema</code>
+ * <li><b>Default:</b> <jk>false</jk>
+ * <li><b>Session-overridable:</b> <jk>false</jk>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link OapiPartParserBuilder#schema(HttpPartSchema)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Defines the OpenAPI schema for this part parser.
+ */
+ public static final String OAPI_schema = PREFIX + "schema.o";
+
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Predefined instances
+ //-------------------------------------------------------------------------------------------------------------------
+
+ /** Reusable instance of {@link OapiPartParser}. */
+ public static final OapiPartParser DEFAULT = new OapiPartParser(PropertyStore.DEFAULT);
+
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Instance
+ //-------------------------------------------------------------------------------------------------------------------
+
+ final HttpPartSchema schema;
+
+ /**
+ * Constructor.
+ *
+ * @param ps The property store containing all the settings for this object.
+ */
+ public OapiPartParser(PropertyStore ps) {
+ super(
+ ps.builder().build()
+ );
+ this.schema = getProperty(OAPI_schema, HttpPartSchema.class, HttpPartSchema.DEFAULT);
+ }
+
+ @Override /* Context */
+ public UonPartParserBuilder builder() {
+ return new UonPartParserBuilder(getPropertyStore());
+ }
+
+ /**
+ * Instantiates a new clean-slate {@link UonPartParserBuilder} object.
+ *
+ * <p>
+ * This is equivalent to simply calling <code><jk>new</jk> UonPartParserBuilder()</code>.
+ *
+ * <p>
+ * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
+ * the settings of the object called on.
+ *
+ * @return A new {@link UonPartParserBuilder} object.
+ */
+ public static UonPartParserBuilder create() {
+ return new UonPartParserBuilder();
+ }
+
+ @Override /* HttpPartParser */
+ public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException {
+ schema = ObjectUtils.firstNonNull(schema, this.schema, HttpPartSchema.DEFAULT);
+ T t = parseInner(partType, schema, in, type);
+ if (t == null && type.isPrimitive())
+ t = type.getPrimitiveDefault();
+ schema.validateOutput(t, this);
+ return t;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ private<T> T parseInner(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException {
+ schema.validateInput(in);
+ if (in == null) {
+ if (schema.getDefault() == null)
+ return null;
+ in = schema.getDefault();
+ } else {
+ switch (schema.getType(type)) {
+ case STRING: {
+ if (type.isObject()) {
+ switch (schema.getFormat()) {
+ case BYTE:
+ return (T)StringUtils.base64Decode(in);
+ case DATE:
+ case DATE_TIME:
+ return (T)StringUtils.parseIsoDate(in);
+ case BINARY:
+ return (T)StringUtils.fromHex(in);
+ case UON:
+ return super.parse(partType, schema, in, type);
+ default:
+ return (T)in;
+ }
+ }
+ switch (schema.getFormat()) {
+ case BYTE:
+ return toType(StringUtils.base64Decode(in), type);
+ case DATE:
+ case DATE_TIME:
+ return toType(StringUtils.parseIsoDate(in), type);
+ case BINARY:
+ return toType(StringUtils.fromHex(in), type);
+ case UON:
+ return super.parse(partType, schema, in, type);
+ default:
+ return toType(in, type);
+ }
+ }
+ case ARRAY: {
+ if (type.isObject())
+ type = (ClassMeta<T>)getClassMeta(ObjectList.class);
+
+ ClassMeta<?> eType = type.isObject() ? string() : type.getElementType();
+ if (eType == null)
+ throw new ParseException("Value of type ARRAY cannot be converted to type {0}", type);
+
+ String[] ss = new String[0];
+ switch (schema.getCollectionFormat()) {
+ case MULTI:
+ ss = new String[]{in};
+ break;
+ case CSV:
+ ss = split(in, ',');
+ break;
+ case PIPES:
+ ss = split(in, '|');
+ break;
+ case SSV:
+ ss = splitQuoted(in);
+ break;
+ case TSV:
+ ss = split(in, '\t');
+ break;
+ case UON:
+ return super.parse(partType, null, in, type);
+ case NONE:
+ if (firstNonWhitespaceChar(in) == '@' && lastNonWhitespaceChar(in) == ')')
+ return super.parse(partType, null, in, type);
+ ss = split(in, ',');
+ }
+ Object[] o = null;
+ if (schema.getItems() != null) {
+ o = new Object[ss.length];
+ for (int i = 0; i < ss.length; i++)
+ o[i] = parse(partType, schema.getItems(), ss[i], eType);
+ } else {
+ o = ss;
+ }
+ return toType(o, type);
+ }
+ case BOOLEAN: {
+ if (type.isObject())
+ type = (ClassMeta<T>)getClassMeta(Boolean.class);
+ return super.parse(partType, schema, in, type);
+ }
+ case INTEGER: {
+ if (type.isObject()) {
+ switch (schema.getFormat()) {
+ case INT64:
+ type = (ClassMeta<T>)getClassMeta(Long.class);
+ break;
+ default:
+ type = (ClassMeta<T>)getClassMeta(Integer.class);
+
+ }
+ }
+ return super.parse(partType, schema, in, type);
+ }
+ case NUMBER: {
+ if (type.isObject()) {
+ switch (schema.getFormat()) {
+ case DOUBLE:
+ type = (ClassMeta<T>)getClassMeta(Double.class);
+ break;
+ default:
+ type = (ClassMeta<T>)getClassMeta(Float.class);
+ }
+ }
+ return super.parse(partType, schema, in, type);
+ }
+ case OBJECT: {
+ if (type.isObject())
+ type = (ClassMeta<T>)getClassMeta(ObjectMap.class);
+ switch (schema.getFormat()) {
+ default:
+ return super.parse(partType, schema, in, type);
+ }
+ }
+ case FILE: {
+ throw new ParseException("File part not supported.");
+ }
+ case NONE: {
+ throw new ParseException("Invalid type.");
+ }
+ }
+ }
+
+ return super.parse(partType, schema, in, type);
+ }
+
+ private <T> T toType(Object in, ClassMeta<T> type) {
+ return createBeanSession().convertToType(in, type);
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParserBuilder.java
similarity index 59%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParserBuilder.java
index 9110e0d..0c100ba 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartParserBuilder.java
@@ -10,26 +10,29 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.oapi;
-import static org.apache.juneau.uon.UonParser.*;
+import static org.apache.juneau.httppart.oapi.OapiPartParser.*;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.http.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.uon.*;
+import org.apache.juneau.urlencoding.*;
/**
- * Builder class for building instances of {@link UonPartParser}.
+ * Builder class for building instances of {@link OapiPartParser}.
*/
-public class UonPartParserBuilder extends UonParserBuilder {
+public class OapiPartParserBuilder extends UonPartParserBuilder {
/**
* Constructor, default settings.
*/
- public UonPartParserBuilder() {
+ public OapiPartParserBuilder() {
super();
}
@@ -38,13 +41,13 @@ public class UonPartParserBuilder extends UonParserBuilder {
*
* @param ps The initial configuration settings for this builder.
*/
- public UonPartParserBuilder(PropertyStore ps) {
+ public OapiPartParserBuilder(PropertyStore ps) {
super(ps);
}
@Override /* ContextBuilder */
- public UonPartParser build() {
- return build(UonPartParser.class);
+ public OapiPartParser build() {
+ return build(OapiPartParser.class);
}
@@ -52,427 +55,448 @@ public class UonPartParserBuilder extends UonParserBuilder {
// Properties
//--------------------------------------------------------------------------------
+ /**
+ * Configuration property: Decode <js>"%xx"</js> sequences.
+ *
+ * <p>
+ * Specify <jk>true</jk> if URI encoded characters should be decoded, <jk>false</jk> if they've already been
+ * decoded before being passed to this parser.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link UonParser#UON_decoding}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>Default is <jk>false</jk> for {@link UonParser}, <jk>true</jk> for {@link UrlEncodingParser}
+ * @return This object (for method chaining).
+ */
+ public OapiPartParserBuilder schema(HttpPartSchema value) {
+ return set(OAPI_schema, value);
+ }
+
@Override /* UonParserBuilder */
- public UonPartParserBuilder decoding(boolean value) {
+ public OapiPartParserBuilder decoding(boolean value) {
return set(UON_decoding, value);
}
@Override /* ParserBuilder */
- public UonPartParserBuilder fileCharset(String value) {
+ public OapiPartParserBuilder fileCharset(String value) {
super.fileCharset(value);
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder inputStreamCharset(String value) {
+ public OapiPartParserBuilder inputStreamCharset(String value) {
super.inputStreamCharset(value);
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder listener(Class<? extends ParserListener> value) {
+ public OapiPartParserBuilder listener(Class<? extends ParserListener> value) {
super.listener(value);
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder strict(boolean value) {
+ public OapiPartParserBuilder strict(boolean value) {
super.strict(value);
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder strict() {
+ public OapiPartParserBuilder strict() {
super.strict();
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder trimStrings(boolean value) {
+ public OapiPartParserBuilder trimStrings(boolean value) {
super.trimStrings(value);
return this;
}
@Override /* ParserBuilder */
- public UonPartParserBuilder trimStrings() {
+ public OapiPartParserBuilder trimStrings() {
super.trimStrings();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanClassVisibility(Visibility value) {
+ public OapiPartParserBuilder beanClassVisibility(Visibility value) {
super.beanClassVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanConstructorVisibility(Visibility value) {
+ public OapiPartParserBuilder beanConstructorVisibility(Visibility value) {
super.beanConstructorVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanDictionary(boolean append, Object...values) {
+ public OapiPartParserBuilder beanDictionary(boolean append, Object...values) {
super.beanDictionary(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanDictionary(Class<?>...values) {
+ public OapiPartParserBuilder beanDictionary(Class<?>...values) {
super.beanDictionary(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanDictionary(Object...values) {
+ public OapiPartParserBuilder beanDictionary(Object...values) {
super.beanDictionary(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanDictionaryRemove(Object...values) {
+ public OapiPartParserBuilder beanDictionaryRemove(Object...values) {
super.beanDictionaryRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanFieldVisibility(Visibility value) {
+ public OapiPartParserBuilder beanFieldVisibility(Visibility value) {
super.beanFieldVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanFilters(boolean append, Object...values) {
+ public OapiPartParserBuilder beanFilters(boolean append, Object...values) {
super.beanFilters(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanFilters(Class<?>...values) {
+ public OapiPartParserBuilder beanFilters(Class<?>...values) {
super.beanFilters(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanFilters(Object...values) {
+ public OapiPartParserBuilder beanFilters(Object...values) {
super.beanFilters(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanFiltersRemove(Object...values) {
+ public OapiPartParserBuilder beanFiltersRemove(Object...values) {
super.beanFiltersRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanMapPutReturnsOldValue(boolean value) {
+ public OapiPartParserBuilder beanMapPutReturnsOldValue(boolean value) {
super.beanMapPutReturnsOldValue(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanMapPutReturnsOldValue() {
+ public OapiPartParserBuilder beanMapPutReturnsOldValue() {
super.beanMapPutReturnsOldValue();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanMethodVisibility(Visibility value) {
+ public OapiPartParserBuilder beanMethodVisibility(Visibility value) {
super.beanMethodVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireDefaultConstructor(boolean value) {
+ public OapiPartParserBuilder beansRequireDefaultConstructor(boolean value) {
super.beansRequireDefaultConstructor(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireDefaultConstructor() {
+ public OapiPartParserBuilder beansRequireDefaultConstructor() {
super.beansRequireDefaultConstructor();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireSerializable(boolean value) {
+ public OapiPartParserBuilder beansRequireSerializable(boolean value) {
super.beansRequireSerializable(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireSerializable() {
+ public OapiPartParserBuilder beansRequireSerializable() {
super.beansRequireSerializable();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireSettersForGetters(boolean value) {
+ public OapiPartParserBuilder beansRequireSettersForGetters(boolean value) {
super.beansRequireSettersForGetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireSettersForGetters() {
+ public OapiPartParserBuilder beansRequireSettersForGetters() {
super.beansRequireSettersForGetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beansRequireSomeProperties(boolean value) {
+ public OapiPartParserBuilder beansRequireSomeProperties(boolean value) {
super.beansRequireSomeProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder beanTypePropertyName(String value) {
+ public OapiPartParserBuilder beanTypePropertyName(String value) {
super.beanTypePropertyName(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder debug() {
+ public OapiPartParserBuilder debug() {
super.debug();
return this;
}
@Override /* BeanContextBuilder */
- public <T> UonPartParserBuilder example(Class<T> c, T o) {
+ public <T> OapiPartParserBuilder example(Class<T> c, T o) {
super.example(c, o);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreInvocationExceptionsOnGetters(boolean value) {
+ public OapiPartParserBuilder ignoreInvocationExceptionsOnGetters(boolean value) {
super.ignoreInvocationExceptionsOnGetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreInvocationExceptionsOnGetters() {
+ public OapiPartParserBuilder ignoreInvocationExceptionsOnGetters() {
super.ignoreInvocationExceptionsOnGetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreInvocationExceptionsOnSetters(boolean value) {
+ public OapiPartParserBuilder ignoreInvocationExceptionsOnSetters(boolean value) {
super.ignoreInvocationExceptionsOnSetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreInvocationExceptionsOnSetters() {
+ public OapiPartParserBuilder ignoreInvocationExceptionsOnSetters() {
super.ignoreInvocationExceptionsOnSetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignorePropertiesWithoutSetters(boolean value) {
+ public OapiPartParserBuilder ignorePropertiesWithoutSetters(boolean value) {
super.ignorePropertiesWithoutSetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreUnknownBeanProperties(boolean value) {
+ public OapiPartParserBuilder ignoreUnknownBeanProperties(boolean value) {
super.ignoreUnknownBeanProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreUnknownBeanProperties() {
+ public OapiPartParserBuilder ignoreUnknownBeanProperties() {
super.ignoreUnknownBeanProperties();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder ignoreUnknownNullBeanProperties(boolean value) {
+ public OapiPartParserBuilder ignoreUnknownNullBeanProperties(boolean value) {
super.ignoreUnknownNullBeanProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public <T> UonPartParserBuilder implClass(Class<T> interfaceClass, Class<? extends T> implClass) {
+ public <T> OapiPartParserBuilder implClass(Class<T> interfaceClass, Class<? extends T> implClass) {
super.implClass(interfaceClass, implClass);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder implClasses(Map<String,Class<?>> values) {
+ public OapiPartParserBuilder implClasses(Map<String,Class<?>> values) {
super.implClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder locale(Locale value) {
+ public OapiPartParserBuilder locale(Locale value) {
super.locale(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder mediaType(MediaType value) {
+ public OapiPartParserBuilder mediaType(MediaType value) {
super.mediaType(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanClasses(boolean append, Object...values) {
+ public OapiPartParserBuilder notBeanClasses(boolean append, Object...values) {
super.notBeanClasses(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanClasses(Class<?>...values) {
+ public OapiPartParserBuilder notBeanClasses(Class<?>...values) {
super.notBeanClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanClasses(Object...values) {
+ public OapiPartParserBuilder notBeanClasses(Object...values) {
super.notBeanClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanClassesRemove(Object...values) {
+ public OapiPartParserBuilder notBeanClassesRemove(Object...values) {
super.notBeanClassesRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanPackages(boolean append, Object...values) {
+ public OapiPartParserBuilder notBeanPackages(boolean append, Object...values) {
super.notBeanPackages(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanPackages(Object...values) {
+ public OapiPartParserBuilder notBeanPackages(Object...values) {
super.notBeanPackages(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanPackages(String...values) {
+ public OapiPartParserBuilder notBeanPackages(String...values) {
super.notBeanPackages(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder notBeanPackagesRemove(Object...values) {
+ public OapiPartParserBuilder notBeanPackagesRemove(Object...values) {
super.notBeanPackagesRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder pojoSwaps(boolean append, Object...values) {
+ public OapiPartParserBuilder pojoSwaps(boolean append, Object...values) {
super.pojoSwaps(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder pojoSwaps(Class<?>...values) {
+ public OapiPartParserBuilder pojoSwaps(Class<?>...values) {
super.pojoSwaps(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder pojoSwaps(Object...values) {
+ public OapiPartParserBuilder pojoSwaps(Object...values) {
super.pojoSwaps(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder pojoSwapsRemove(Object...values) {
+ public OapiPartParserBuilder pojoSwapsRemove(Object...values) {
super.pojoSwapsRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder sortProperties(boolean value) {
+ public OapiPartParserBuilder sortProperties(boolean value) {
super.sortProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder sortProperties() {
+ public OapiPartParserBuilder sortProperties() {
super.sortProperties();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder timeZone(TimeZone value) {
+ public OapiPartParserBuilder timeZone(TimeZone value) {
super.timeZone(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder useEnumNames() {
+ public OapiPartParserBuilder useEnumNames() {
super.useEnumNames();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder useInterfaceProxies(boolean value) {
+ public OapiPartParserBuilder useInterfaceProxies(boolean value) {
super.useInterfaceProxies(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder useJavaBeanIntrospector(boolean value) {
+ public OapiPartParserBuilder useJavaBeanIntrospector(boolean value) {
super.useJavaBeanIntrospector(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartParserBuilder useJavaBeanIntrospector() {
+ public OapiPartParserBuilder useJavaBeanIntrospector() {
super.useJavaBeanIntrospector();
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder set(String name, Object value) {
+ public OapiPartParserBuilder set(String name, Object value) {
super.set(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder set(boolean append, String name, Object value) {
+ public OapiPartParserBuilder set(boolean append, String name, Object value) {
super.set(append, name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder set(Map<String,Object> properties) {
+ public OapiPartParserBuilder set(Map<String,Object> properties) {
super.set(properties);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder add(Map<String,Object> properties) {
+ public OapiPartParserBuilder add(Map<String,Object> properties) {
super.add(properties);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder addTo(String name, Object value) {
+ public OapiPartParserBuilder addTo(String name, Object value) {
super.addTo(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder addTo(String name, String key, Object value) {
+ public OapiPartParserBuilder addTo(String name, String key, Object value) {
super.addTo(name, key, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder removeFrom(String name, Object value) {
+ public OapiPartParserBuilder removeFrom(String name, Object value) {
super.removeFrom(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartParserBuilder apply(PropertyStore copyFrom) {
+ public OapiPartParserBuilder apply(PropertyStore copyFrom) {
super.apply(copyFrom);
return this;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializer.java
similarity index 65%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializer.java
index 0d9ed56..b449d94 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializer.java
@@ -10,13 +10,12 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
-
-import java.io.*;
+package org.apache.juneau.httppart.oapi;
import org.apache.juneau.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.internal.*;
-import org.apache.juneau.uon.*;
/**
* Serializes POJOs to values suitable for transmission as HTTP headers, query/form-data parameters, and path variables.
@@ -25,32 +24,63 @@ import org.apache.juneau.uon.*;
* This serializer uses UON notation for all parts by default. This allows for arbitrary POJOs to be losslessly
* serialized as any of the specified HTTP types.
*/
-public class UonPartSerializer extends UonSerializer implements HttpPartSerializer {
+public class OapiPartSerializer extends UonPartSerializer {
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Configurable properties
+ //-------------------------------------------------------------------------------------------------------------------
+
+ private static final String PREFIX = "OapiPartSerializer.";
+
+ /**
+ * Configuration property: OpenAPI schema description.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul>
+ * <li><b>Name:</b> <js>"OapiPartSerializer.schema"</js>
+ * <li><b>Data type:</b> <code>HttpPartSchema</code>
+ * <li><b>Default:</b> <jk>false</jk>
+ * <li><b>Session-overridable:</b> <jk>false</jk>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link OapiPartSerializerBuilder#schema(HttpPartSchema)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Defines the OpenAPI schema for this part serializer.
+ */
+ public static final String OAPI_schema = PREFIX + "schema.o";
+
//-------------------------------------------------------------------------------------------------------------------
// Predefined instances
//-------------------------------------------------------------------------------------------------------------------
- /** Reusable instance of {@link UonPartSerializer}, all default settings. */
- public static final UonPartSerializer DEFAULT = new UonPartSerializer(PropertyStore.DEFAULT);
+ /** Reusable instance of {@link OapiPartSerializer}, all default settings. */
+ public static final OapiPartSerializer DEFAULT = new OapiPartSerializer(PropertyStore.DEFAULT);
//-------------------------------------------------------------------------------------------------------------------
// Instance
//-------------------------------------------------------------------------------------------------------------------
+ final HttpPartSchema schema;
+
/**
* Constructor.
*
* @param ps
* The property store containing all the settings for this object.
*/
- public UonPartSerializer(PropertyStore ps) {
+ public OapiPartSerializer(PropertyStore ps) {
super(
ps.builder()
.set(UON_encoding, false)
.build()
);
+ this.schema = getProperty(OAPI_schema, HttpPartSchema.class, HttpPartSchema.DEFAULT);
}
@Override /* Context */
@@ -76,25 +106,8 @@ public class UonPartSerializer extends UonSerializer implements HttpPartSerializ
//--------------------------------------------------------------------------------
@Override /* PartSerializer */
- public String serialize(HttpPartType type, Object value) {
- try {
- // Shortcut for simple types.
- ClassMeta<?> cm = getClassMetaForObject(value);
- if (cm != null) {
- if (cm.isNumber() || cm.isBoolean())
- return ClassUtils.toString(value);
- if (cm.isString()) {
- String s = ClassUtils.toString(value);
- if (s.isEmpty() || ! UonUtils.needsQuotes(s))
- return s;
- }
- }
- StringWriter w = new StringWriter();
- UonSerializerSession s = new UonSerializerSession(this, false, createDefaultSessionArgs());
- s.serialize(value, w);
- return w.toString();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
+ schema = ObjectUtils.firstNonNull(schema, this.schema, HttpPartSchema.DEFAULT);
+ return super.serialize(type, schema, value);
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializerBuilder.java
similarity index 58%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializerBuilder.java
index c26cbf8..7d232ab 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/oapi/OapiPartSerializerBuilder.java
@@ -10,24 +10,29 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.oapi;
+
+import static org.apache.juneau.httppart.oapi.OapiPartSerializer.*;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.http.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.uon.*;
+import org.apache.juneau.urlencoding.*;
/**
* Builder class for building instances of {@link UonPartSerializer}.
*/
-public class UonPartSerializerBuilder extends UonSerializerBuilder {
+public class OapiPartSerializerBuilder extends UonPartSerializerBuilder {
/**
* Constructor, default settings.
*/
- public UonPartSerializerBuilder() {
+ public OapiPartSerializerBuilder() {
super();
}
@@ -36,13 +41,13 @@ public class UonPartSerializerBuilder extends UonSerializerBuilder {
*
* @param ps The initial configuration settings for this builder.
*/
- public UonPartSerializerBuilder(PropertyStore ps) {
+ public OapiPartSerializerBuilder(PropertyStore ps) {
super(ps);
}
@Override /* ContextBuilder */
- public UonPartSerializer build() {
- return build(UonPartSerializer.class);
+ public OapiPartSerializer build() {
+ return build(OapiPartSerializer.class);
}
@@ -50,565 +55,586 @@ public class UonPartSerializerBuilder extends UonSerializerBuilder {
// Properties
//--------------------------------------------------------------------------------
+ /**
+ * Configuration property: Decode <js>"%xx"</js> sequences.
+ *
+ * <p>
+ * Specify <jk>true</jk> if URI encoded characters should be decoded, <jk>false</jk> if they've already been
+ * decoded before being passed to this parser.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jf'>{@link UonParser#UON_decoding}
+ * </ul>
+ *
+ * @param value
+ * The new value for this property.
+ * <br>Default is <jk>false</jk> for {@link UonParser}, <jk>true</jk> for {@link UrlEncodingParser}
+ * @return This object (for method chaining).
+ */
+ public OapiPartSerializerBuilder schema(HttpPartSchema value) {
+ return set(OAPI_schema, value);
+ }
+
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder addBeanTypes(boolean value) {
+ public OapiPartSerializerBuilder addBeanTypes(boolean value) {
super.addBeanTypes(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder addBeanTypes() {
+ public OapiPartSerializerBuilder addBeanTypes() {
super.addBeanTypes();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder addRootType(boolean value) {
+ public OapiPartSerializerBuilder addRootType(boolean value) {
super.addRootType(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder addRootType() {
+ public OapiPartSerializerBuilder addRootType() {
super.addRootType();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder detectRecursions(boolean value) {
+ public OapiPartSerializerBuilder detectRecursions(boolean value) {
super.detectRecursions(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder detectRecursions() {
+ public OapiPartSerializerBuilder detectRecursions() {
super.detectRecursions();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder ignoreRecursions(boolean value) {
+ public OapiPartSerializerBuilder ignoreRecursions(boolean value) {
super.ignoreRecursions(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder ignoreRecursions() {
+ public OapiPartSerializerBuilder ignoreRecursions() {
super.ignoreRecursions();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder initialDepth(int value) {
+ public OapiPartSerializerBuilder initialDepth(int value) {
super.initialDepth(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder listener(Class<? extends SerializerListener> value) {
+ public OapiPartSerializerBuilder listener(Class<? extends SerializerListener> value) {
super.listener(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder maxDepth(int value) {
+ public OapiPartSerializerBuilder maxDepth(int value) {
super.maxDepth(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder maxIndent(int value) {
+ public OapiPartSerializerBuilder maxIndent(int value) {
super.maxIndent(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder quoteChar(char value) {
+ public OapiPartSerializerBuilder quoteChar(char value) {
super.quoteChar(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder sortCollections(boolean value) {
+ public OapiPartSerializerBuilder sortCollections(boolean value) {
super.sortCollections(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder sortCollections() {
+ public OapiPartSerializerBuilder sortCollections() {
super.sortCollections();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder sortMaps(boolean value) {
+ public OapiPartSerializerBuilder sortMaps(boolean value) {
super.sortMaps(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder sortMaps() {
+ public OapiPartSerializerBuilder sortMaps() {
super.sortMaps();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder sq() {
+ public OapiPartSerializerBuilder sq() {
super.sq();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimEmptyCollections(boolean value) {
+ public OapiPartSerializerBuilder trimEmptyCollections(boolean value) {
super.trimEmptyCollections(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimEmptyCollections() {
+ public OapiPartSerializerBuilder trimEmptyCollections() {
super.trimEmptyCollections();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimEmptyMaps(boolean value) {
+ public OapiPartSerializerBuilder trimEmptyMaps(boolean value) {
super.trimEmptyMaps(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimEmptyMaps() {
+ public OapiPartSerializerBuilder trimEmptyMaps() {
super.trimEmptyMaps();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimNullProperties(boolean value) {
+ public OapiPartSerializerBuilder trimNullProperties(boolean value) {
super.trimNullProperties(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimStrings(boolean value) {
+ public OapiPartSerializerBuilder trimStrings(boolean value) {
super.trimStrings(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder trimStrings() {
+ public OapiPartSerializerBuilder trimStrings() {
super.trimStrings();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder uriContext(UriContext value) {
+ public OapiPartSerializerBuilder uriContext(UriContext value) {
super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder uriRelativity(UriRelativity value) {
+ public OapiPartSerializerBuilder uriRelativity(UriRelativity value) {
super.uriRelativity(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder uriResolution(UriResolution value) {
+ public OapiPartSerializerBuilder uriResolution(UriResolution value) {
super.uriResolution(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder useWhitespace(boolean value) {
+ public OapiPartSerializerBuilder useWhitespace(boolean value) {
super.useWhitespace(value);
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder useWhitespace() {
+ public OapiPartSerializerBuilder useWhitespace() {
super.useWhitespace();
return this;
}
@Override /* SerializerBuilder */
- public UonPartSerializerBuilder ws() {
+ public OapiPartSerializerBuilder ws() {
super.ws();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanClassVisibility(Visibility value) {
+ public OapiPartSerializerBuilder beanClassVisibility(Visibility value) {
super.beanClassVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanConstructorVisibility(Visibility value) {
+ public OapiPartSerializerBuilder beanConstructorVisibility(Visibility value) {
super.beanConstructorVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanDictionary(boolean append, Object...values) {
+ public OapiPartSerializerBuilder beanDictionary(boolean append, Object...values) {
super.beanDictionary(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanDictionary(Class<?>...values) {
+ public OapiPartSerializerBuilder beanDictionary(Class<?>...values) {
super.beanDictionary(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanDictionary(Object...values) {
+ public OapiPartSerializerBuilder beanDictionary(Object...values) {
super.beanDictionary(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanDictionaryRemove(Object...values) {
+ public OapiPartSerializerBuilder beanDictionaryRemove(Object...values) {
super.beanDictionaryRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanFieldVisibility(Visibility value) {
+ public OapiPartSerializerBuilder beanFieldVisibility(Visibility value) {
super.beanFieldVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanFilters(boolean append, Object...values) {
+ public OapiPartSerializerBuilder beanFilters(boolean append, Object...values) {
super.beanFilters(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanFilters(Class<?>...values) {
+ public OapiPartSerializerBuilder beanFilters(Class<?>...values) {
super.beanFilters(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanFilters(Object...values) {
+ public OapiPartSerializerBuilder beanFilters(Object...values) {
super.beanFilters(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanFiltersRemove(Object...values) {
+ public OapiPartSerializerBuilder beanFiltersRemove(Object...values) {
super.beanFiltersRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanMapPutReturnsOldValue(boolean value) {
+ public OapiPartSerializerBuilder beanMapPutReturnsOldValue(boolean value) {
super.beanMapPutReturnsOldValue(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanMapPutReturnsOldValue() {
+ public OapiPartSerializerBuilder beanMapPutReturnsOldValue() {
super.beanMapPutReturnsOldValue();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanMethodVisibility(Visibility value) {
+ public OapiPartSerializerBuilder beanMethodVisibility(Visibility value) {
super.beanMethodVisibility(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireDefaultConstructor(boolean value) {
+ public OapiPartSerializerBuilder beansRequireDefaultConstructor(boolean value) {
super.beansRequireDefaultConstructor(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireDefaultConstructor() {
+ public OapiPartSerializerBuilder beansRequireDefaultConstructor() {
super.beansRequireDefaultConstructor();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireSerializable(boolean value) {
+ public OapiPartSerializerBuilder beansRequireSerializable(boolean value) {
super.beansRequireSerializable(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireSerializable() {
+ public OapiPartSerializerBuilder beansRequireSerializable() {
super.beansRequireSerializable();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireSettersForGetters(boolean value) {
+ public OapiPartSerializerBuilder beansRequireSettersForGetters(boolean value) {
super.beansRequireSettersForGetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireSettersForGetters() {
+ public OapiPartSerializerBuilder beansRequireSettersForGetters() {
super.beansRequireSettersForGetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beansRequireSomeProperties(boolean value) {
+ public OapiPartSerializerBuilder beansRequireSomeProperties(boolean value) {
super.beansRequireSomeProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder beanTypePropertyName(String value) {
+ public OapiPartSerializerBuilder beanTypePropertyName(String value) {
super.beanTypePropertyName(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder debug() {
+ public OapiPartSerializerBuilder debug() {
super.debug();
return this;
}
@Override /* BeanContextBuilder */
- public <T> UonPartSerializerBuilder example(Class<T> c, T o) {
+ public <T> OapiPartSerializerBuilder example(Class<T> c, T o) {
super.example(c, o);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreInvocationExceptionsOnGetters(boolean value) {
+ public OapiPartSerializerBuilder ignoreInvocationExceptionsOnGetters(boolean value) {
super.ignoreInvocationExceptionsOnGetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreInvocationExceptionsOnGetters() {
+ public OapiPartSerializerBuilder ignoreInvocationExceptionsOnGetters() {
super.ignoreInvocationExceptionsOnGetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreInvocationExceptionsOnSetters(boolean value) {
+ public OapiPartSerializerBuilder ignoreInvocationExceptionsOnSetters(boolean value) {
super.ignoreInvocationExceptionsOnSetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreInvocationExceptionsOnSetters() {
+ public OapiPartSerializerBuilder ignoreInvocationExceptionsOnSetters() {
super.ignoreInvocationExceptionsOnSetters();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignorePropertiesWithoutSetters(boolean value) {
+ public OapiPartSerializerBuilder ignorePropertiesWithoutSetters(boolean value) {
super.ignorePropertiesWithoutSetters(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreUnknownBeanProperties(boolean value) {
+ public OapiPartSerializerBuilder ignoreUnknownBeanProperties(boolean value) {
super.ignoreUnknownBeanProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreUnknownBeanProperties() {
+ public OapiPartSerializerBuilder ignoreUnknownBeanProperties() {
super.ignoreUnknownBeanProperties();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder ignoreUnknownNullBeanProperties(boolean value) {
+ public OapiPartSerializerBuilder ignoreUnknownNullBeanProperties(boolean value) {
super.ignoreUnknownNullBeanProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public <T> UonPartSerializerBuilder implClass(Class<T> interfaceClass, Class<? extends T> implClass) {
+ public <T> OapiPartSerializerBuilder implClass(Class<T> interfaceClass, Class<? extends T> implClass) {
super.implClass(interfaceClass, implClass);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder implClasses(Map<String,Class<?>> values) {
+ public OapiPartSerializerBuilder implClasses(Map<String,Class<?>> values) {
super.implClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder locale(Locale value) {
+ public OapiPartSerializerBuilder locale(Locale value) {
super.locale(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder mediaType(MediaType value) {
+ public OapiPartSerializerBuilder mediaType(MediaType value) {
super.mediaType(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanClasses(boolean append, Object...values) {
+ public OapiPartSerializerBuilder notBeanClasses(boolean append, Object...values) {
super.notBeanClasses(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanClasses(Class<?>...values) {
+ public OapiPartSerializerBuilder notBeanClasses(Class<?>...values) {
super.notBeanClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanClasses(Object...values) {
+ public OapiPartSerializerBuilder notBeanClasses(Object...values) {
super.notBeanClasses(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanClassesRemove(Object...values) {
+ public OapiPartSerializerBuilder notBeanClassesRemove(Object...values) {
super.notBeanClassesRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanPackages(boolean append, Object...values) {
+ public OapiPartSerializerBuilder notBeanPackages(boolean append, Object...values) {
super.notBeanPackages(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanPackages(Object...values) {
+ public OapiPartSerializerBuilder notBeanPackages(Object...values) {
super.notBeanPackages(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanPackages(String...values) {
+ public OapiPartSerializerBuilder notBeanPackages(String...values) {
super.notBeanPackages(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder notBeanPackagesRemove(Object...values) {
+ public OapiPartSerializerBuilder notBeanPackagesRemove(Object...values) {
super.notBeanPackagesRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder pojoSwaps(boolean append, Object...values) {
+ public OapiPartSerializerBuilder pojoSwaps(boolean append, Object...values) {
super.pojoSwaps(append, values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder pojoSwaps(Class<?>...values) {
+ public OapiPartSerializerBuilder pojoSwaps(Class<?>...values) {
super.pojoSwaps(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder pojoSwaps(Object...values) {
+ public OapiPartSerializerBuilder pojoSwaps(Object...values) {
super.pojoSwaps(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder pojoSwapsRemove(Object...values) {
+ public OapiPartSerializerBuilder pojoSwapsRemove(Object...values) {
super.pojoSwapsRemove(values);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder sortProperties(boolean value) {
+ public OapiPartSerializerBuilder sortProperties(boolean value) {
super.sortProperties(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder sortProperties() {
+ public OapiPartSerializerBuilder sortProperties() {
super.sortProperties();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder timeZone(TimeZone value) {
+ public OapiPartSerializerBuilder timeZone(TimeZone value) {
super.timeZone(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder useEnumNames() {
+ public OapiPartSerializerBuilder useEnumNames() {
super.useEnumNames();
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder useInterfaceProxies(boolean value) {
+ public OapiPartSerializerBuilder useInterfaceProxies(boolean value) {
super.useInterfaceProxies(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder useJavaBeanIntrospector(boolean value) {
+ public OapiPartSerializerBuilder useJavaBeanIntrospector(boolean value) {
super.useJavaBeanIntrospector(value);
return this;
}
@Override /* BeanContextBuilder */
- public UonPartSerializerBuilder useJavaBeanIntrospector() {
+ public OapiPartSerializerBuilder useJavaBeanIntrospector() {
super.useJavaBeanIntrospector();
return this;
}
@Override /* ContextBuilder */
- public UonSerializerBuilder set(String name, Object value) {
+ public OapiPartSerializerBuilder set(String name, Object value) {
super.set(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder set(boolean append, String name, Object value) {
+ public OapiPartSerializerBuilder set(boolean append, String name, Object value) {
super.set(append, name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder set(Map<String,Object> properties) {
+ public OapiPartSerializerBuilder set(Map<String,Object> properties) {
super.set(properties);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder add(Map<String,Object> properties) {
+ public OapiPartSerializerBuilder add(Map<String,Object> properties) {
super.add(properties);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder addTo(String name, Object value) {
+ public OapiPartSerializerBuilder addTo(String name, Object value) {
super.addTo(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder addTo(String name, String key, Object value) {
+ public OapiPartSerializerBuilder addTo(String name, String key, Object value) {
super.addTo(name, key, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder removeFrom(String name, Object value) {
+ public OapiPartSerializerBuilder removeFrom(String name, Object value) {
super.removeFrom(name, value);
return this;
}
@Override /* ContextBuilder */
- public UonPartSerializerBuilder apply(PropertyStore copyFrom) {
+ public OapiPartSerializerBuilder apply(PropertyStore copyFrom) {
super.apply(copyFrom);
return this;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializer.java
similarity index 97%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializer.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializer.java
index b69b16f..6924723 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializer.java
@@ -10,7 +10,7 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import org.apache.juneau.*;
import org.apache.juneau.uon.*;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializerBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializerBuilder.java
similarity index 99%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializerBuilder.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializerBuilder.java
index 9ee32d7..40be466 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/SimpleUonPartSerializerBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/SimpleUonPartSerializerBuilder.java
@@ -10,7 +10,7 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import java.util.*;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParser.java
similarity index 87%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParser.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParser.java
index ec8d320..0f674ec 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParser.java
@@ -10,11 +10,12 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import static org.apache.juneau.internal.StringUtils.*;
import org.apache.juneau.*;
+import org.apache.juneau.httppart.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.uon.*;
@@ -73,9 +74,21 @@ public class UonPartParser extends UonParser implements HttpPartParser {
return new UonPartParserBuilder();
}
-
- @Override /* HttpPartParser */
+ /**
+ * Convenience method for calling the parse method without a schema object.
+ *
+ * @param partType The part type being parsed.
+ * @param in The input being parsed.
+ * @param type The category of value being parsed.
+ * @return The parsed value.
+ * @throws ParseException
+ */
public <T> T parse(HttpPartType partType, String in, ClassMeta<T> type) throws ParseException {
+ return parse(partType, null, in, type);
+ }
+
+ @Override /* HttpPartParser */
+ public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException {
if (in == null)
return null;
if (type.isString() && in.length() > 0) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParserBuilder.java
similarity index 99%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParserBuilder.java
index 9110e0d..7f0c67b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartParserBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartParserBuilder.java
@@ -10,7 +10,7 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import static org.apache.juneau.uon.UonParser.*;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializer.java
similarity index 88%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializer.java
index 0d9ed56..96657bc 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializer.java
@@ -10,11 +10,12 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import java.io.*;
import org.apache.juneau.*;
+import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.uon.*;
@@ -75,8 +76,19 @@ public class UonPartSerializer extends UonSerializer implements HttpPartSerializ
// Entry point methods
//--------------------------------------------------------------------------------
- @Override /* PartSerializer */
+ /**
+ * Convenience method for calling the parse method without a schema object.
+ *
+ * @param type The category of value being serialized.
+ * @param value The value being serialized.
+ * @return The serialized value.
+ */
public String serialize(HttpPartType type, Object value) {
+ return serialize(type, null, value);
+ }
+
+ @Override /* PartSerializer */
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
try {
// Shortcut for simple types.
ClassMeta<?> cm = getClassMetaForObject(value);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializerBuilder.java
similarity index 99%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializerBuilder.java
index c26cbf8..6e14ad1 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/UonPartSerializerBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/uon/UonPartSerializerBuilder.java
@@ -10,7 +10,7 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.httppart;
+package org.apache.juneau.httppart.uon;
import java.util.*;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
index 57cfce3..a92ff8a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.internal;
+import static org.apache.juneau.internal.StringUtils.*;
+
import java.lang.ref.*;
import java.text.*;
import java.util.*;
@@ -114,7 +116,7 @@ public final class DateUtils {
* @return The parsed value, or <jk>null</jk> if the string was <jk>null</jk> or empty.
*/
public static Calendar parseISO8601Calendar(String s) {
- if (StringUtils.isEmpty(s))
+ if (isEmpty(s))
return null;
return DatatypeConverter.parseDateTime(toValidISO8601DT(s));
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
index 606d7f8..ad0780c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -13,6 +13,7 @@
package org.apache.juneau.internal;
import static org.apache.juneau.internal.ThrowableUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
import java.io.*;
import java.nio.charset.*;
@@ -364,7 +365,7 @@ public final class IOUtils {
*/
public static int getBufferSize(String contentLength) {
try {
- if (! StringUtils.isEmpty(contentLength)) {
+ if (isNotEmpty(contentLength)) {
long l = Long.decode(contentLength);
if (l > 1048576)
return 1048576;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/JuneauLogger.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/JuneauLogger.java
index 77100f9..9edf71f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/JuneauLogger.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/JuneauLogger.java
@@ -272,7 +272,7 @@ public class JuneauLogger extends java.util.logging.Logger {
}
private static String resolveResourceBundleName(Class<?> forClass, String path) {
- if (StringUtils.isEmpty(path))
+ if (isEmpty(path))
return null;
String rb = rbMap.get(forClass);
if (rb == null) {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index f1d7c09..d679771 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -802,6 +802,28 @@ public final class StringUtils {
}
/**
+ * Returns <jk>true</jk> if specified string is not <jk>null</jk> or empty.
+ *
+ * @param s The string to check.
+ * @return <jk>true</jk> if specified string is not <jk>null</jk> or empty.
+ */
+ public static boolean isNotEmpty(String s) {
+ return ! isEmpty(s);
+ }
+
+ /**
+ * Returns <jk>true</jk> if specified string is not <jk>null</jk> or it's {@link #toString()} method doesn't return an empty
+ * string.
+ *
+ * @param s The string to check.
+ * @return
+ * <jk>true</jk> if specified string is not <jk>null</jk> or it's {@link #toString()} method doesn't return an empty string.
+ */
+ public static boolean isNotEmpty(Object s) {
+ return ! isEmpty(s);
+ }
+
+ /**
* Returns <jk>null</jk> if the specified string is <jk>null</jk> or empty.
*
* @param s The string to check.
@@ -2010,7 +2032,7 @@ public final class StringUtils {
*/
public static String firstNonEmpty(String...s) {
for (String ss : s)
- if (! isEmpty(ss))
+ if (isNotEmpty(ss))
return ss;
return null;
}
@@ -2163,6 +2185,47 @@ public final class StringUtils {
}
/**
+ * Parses a string that can consist of either a JSON array or comma-delimited list.
+ *
+ * <p>
+ * The type of string is auto-detected.
+ *
+ * @param s The string to parse.
+ * @return The parsed string.
+ * @throws ParseException
+ */
+ public static ObjectList parseListOrCdl(String s) throws ParseException {
+ if (isEmpty(s))
+ return null;
+ if (! isObjectList(s, true))
+ return new ObjectList(Arrays.asList(StringUtils.split(s.trim(), ',')));
+ return new ObjectList(s);
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified string is valid JSON.
+ *
+ * <p>
+ * Leading and trailing spaces are ignored.
+ * <br>Leading and trailing comments are not allowed.
+ *
+ * @param s The string to test.
+ * @return <jk>true</jk> if the specified string is valid JSON.
+ */
+ public static boolean isJson(String s) {
+ if (s == null)
+ return false;
+ char c1 = StringUtils.firstNonWhitespaceChar(s), c2 = StringUtils.lastNonWhitespaceChar(s);
+ if (c1 == '{' && c2 == '}' || c1 == '[' && c2 == ']' || c1 == '\'' && c2 == '\'')
+ return true;
+ if (StringUtils.isOneOf(s, "true","false","null"))
+ return true;
+ if (StringUtils.isNumeric(s))
+ return true;
+ return false;
+ }
+
+ /**
* Returns <jk>true</jk> if the specified string appears to be a JSON object.
*
* @param o The object to test.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/VersionRange.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/VersionRange.java
index bf8c7e5..43ff5ff 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/VersionRange.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/VersionRange.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.internal;
+import static org.apache.juneau.internal.StringUtils.*;
+
/**
* Represents an OSGi-style version range like <js>"1.2"</js> or <js>"[1.0,2.0)"</js>.
*
@@ -62,7 +64,7 @@ public class VersionRange {
* @return <jk>true</jk> if the specified version string matches this version range.
*/
public boolean matches(String v) {
- if (StringUtils.isEmpty(v))
+ if (isEmpty(v))
return (minVersion == null && maxVersion == null);
Version ver = new Version(v);
if (minVersion != null && ! ver.isAtLeast(minVersion, minExclusive))
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
index 290090a..4332c79 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -260,7 +260,7 @@ public class JsonSerializer extends WriterSerializer {
/** Default serializer, all default settings.*/
public static final JsonSerializer DEFAULT_READABLE = new Readable(PropertyStore.DEFAULT);
- /** Default serializer, single quotes, simple mode. */
+ /** Default serializer, single quotes, {@link #JSON_simpleMode simple mode}. */
public static final JsonSerializer DEFAULT_LAX = new Simple(PropertyStore.DEFAULT);
/** Default serializer, single quotes, simple mode, with whitespace. */
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
index b391bf5..8f6faaf 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/AList.java
@@ -60,4 +60,17 @@ public final class AList<T> extends LinkedList<T> {
addAll(Arrays.asList(t));
return this;
}
+
+ /**
+ * Adds an entry to this list if the boolean flag is <jk>true</jk>.
+ *
+ * @param b The boolean flag.
+ * @param val The value to add.
+ * @return This object (for method chaining).
+ */
+ public AList<T> appendIf(boolean b, T val) {
+ if (b)
+ append(val);
+ return this;
+ }
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/CalendarUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/CalendarUtils.java
index a6de8a9..c174920 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/CalendarUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/CalendarUtils.java
@@ -13,6 +13,7 @@
package org.apache.juneau.utils;
import static org.apache.juneau.internal.DateUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
import java.text.*;
import java.util.*;
@@ -20,8 +21,6 @@ import java.util.concurrent.*;
import javax.xml.bind.*;
-import org.apache.juneau.internal.*;
-
/**
* Utility class for converting {@link Calendar} and {@link Date} objects to common serialized forms.
*/
@@ -560,7 +559,7 @@ public class CalendarUtils {
* @throws Exception
*/
public static final Calendar parseCalendar(String in, CalendarUtils.Format format, Locale locale, TimeZone timeZone) throws Exception {
- if (StringUtils.isEmpty(in))
+ if (isEmpty(in))
return null;
if (timeZone == null)
timeZone = TimeZone.getDefault();
@@ -632,7 +631,7 @@ public class CalendarUtils {
* @throws Exception
*/
public static final Date parseDate(String in, CalendarUtils.Format format, Locale locale, TimeZone timeZone) throws Exception {
- if (StringUtils.isEmpty(in))
+ if (isEmpty(in))
return null;
if (timeZone == null)
timeZone = TimeZone.getDefault();
diff --git a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/ArgsVar.java b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/ArgsVar.java
index b0b45f5..c1159cd 100644
--- a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/ArgsVar.java
+++ b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/vars/ArgsVar.java
@@ -79,7 +79,7 @@ public class ArgsVar extends DefaultingVar {
this.args = ARGS;
else {
String s = System.getProperty("sun.java.command");
- if (! isEmpty(s)) {
+ if (isNotEmpty(s)) {
int i = s.indexOf(' ');
args = new Args(i == -1 ? "" : s.substring(i+1));
} else {
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
index d903e2b..4298914 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
@@ -279,7 +279,7 @@ public class PetStoreResource extends BasicRestServletJena {
@Query(
name="status",
description="Status values that need to be considered for filter.",
- required="true",
+ required=true,
type="array",
items=@Items(
type="string",
@@ -311,7 +311,7 @@ public class PetStoreResource extends BasicRestServletJena {
@Query(
name="tags",
description="Tags to filter by",
- required="true",
+ required=true,
example="['tag1','tag2']"
)
String[] tags
@@ -332,7 +332,7 @@ public class PetStoreResource extends BasicRestServletJena {
)
)
public Ok deletePet(
- @Header(name="api_key", description="Security API key", required="true", example="foobar") String apiKey,
+ @Header(name="api_key", description="Security API key", required=true, example="foobar") String apiKey,
@Path(name="petId", description="Pet id to delete", example="123") long petId
) throws IdNotFound, NotAcceptable {
@@ -354,7 +354,7 @@ public class PetStoreResource extends BasicRestServletJena {
public Ok uploadImage(
@Path(name="petId", description="ID of pet to update", example="123") long petId,
@FormData(name="additionalMetadata", description="Additional data to pass to server", example="Foobar") String additionalMetadata,
- @FormData(name="file", description="file to upload", required="true", type="file") byte[] file
+ @FormData(name="file", description="file to upload", required=true, type="file") byte[] file
) throws NotAcceptable, UnsupportedMediaType {
return OK;
@@ -591,8 +591,8 @@ public class PetStoreResource extends BasicRestServletJena {
)
)
public Ok login(
- @Query(name="username", description="The username for login", required="true", example="myuser") String username,
- @Query(name="password", description="The password for login in clear text", required="true", example="abc123") String password,
+ @Query(name="username", description="The username for login", required=true, example="myuser") String username,
+ @Query(name="password", description="The password for login in clear text", required=true, example="abc123") String password,
@ResponseHeader(name="X-Rate-Limit", type="integer", format="int32", description="Calls per hour allowed by the user.", example="123") Value<Integer> rateLimit,
ExpiresAfter expiresAfter,
RestRequest req,
diff --git a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
index 9d609bd..9a827dd 100755
--- a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
+++ b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
@@ -761,7 +761,7 @@ public abstract class Microservice implements ConfigEventListener {
Config cf = getConfig();
logger = Logger.getLogger("");
String logFile = cf.getString("Logging/logFile");
- if (! isEmpty(logFile)) {
+ if (isNotEmpty(logFile)) {
LogManager.getLogManager().reset();
String logDir = cf.getString("Logging/logDir", ".");
mkdirs(new File(logDir), false);
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
index cd602f0..34079b6 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
@@ -22,6 +22,7 @@ import java.util.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.remoteable.*;
import org.apache.juneau.remoteable.FormData;
import org.apache.juneau.remoteable.Header;
@@ -1998,7 +1999,7 @@ public class RequestBeanProxyTest {
public static class XSerializer implements HttpPartSerializer {
@Override
- public String serialize(HttpPartType type, Object value) {
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
if (value == null)
return "NULL";
if (value instanceof Collection)
@@ -2011,7 +2012,7 @@ public class RequestBeanProxyTest {
public static class ListSerializer implements HttpPartSerializer {
@Override
- public String serialize(HttpPartType type, Object value) {
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
if (value == null)
return "NULL";
if (value instanceof Collection)
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
index d843659..8454f34 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
@@ -22,6 +22,7 @@ import java.util.concurrent.atomic.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.html.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.jena.*;
import org.apache.juneau.json.*;
import org.apache.juneau.msgpack.*;
@@ -3847,7 +3848,7 @@ public class ThirdPartyProxyTest extends RestTestcase {
public static class DummyPartSerializer implements HttpPartSerializer {
@Override
- public String serialize(HttpPartType type, Object value) {
+ public String serialize(HttpPartType type, HttpPartSchema schema, Object value) {
return "dummy-"+value;
}
}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
index 0fc7067..52b89ec 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
@@ -20,6 +20,7 @@ import org.apache.http.*;
import org.apache.http.client.entity.*;
import org.apache.http.message.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -76,11 +77,17 @@ public final class NameValuePairs extends LinkedList<NameValuePair> {
*
* @param name The pair name.
* @param value The pair value.
- * @param partSerializer The serializer to use for converting values to simple strings.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the serializer parser is not a subclass of {@link OapiPartSerializer}.
* @return This object (for method chaining).
*/
- public NameValuePairs append(String name, Object value, HttpPartSerializer partSerializer) {
- super.add(new SerializedNameValuePair(name, value, partSerializer));
+ public NameValuePairs append(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema) {
+ super.add(new SerializedNameValuePair(name, value, serializer, schema));
return this;
}
}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
index 4a31b43..9d01ea7 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
@@ -37,6 +37,8 @@ import org.apache.juneau.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.parser.ParseException;
@@ -193,26 +195,31 @@ public final class RestCall extends BeanSession implements Closeable {
* Can also be {@link Map}, {@link String}, {@link NameValuePairs}, or bean if the name is null/blank/*.
* If a {@link String} and the name is null/blank/*, then calls {@link URIBuilder#setCustomQuery(String)}.
* @param skipIfEmpty Don't add the pair if the value is empty.
- * @param partSerializer
- * The part serializer to use to convert the value to a string.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
* If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part serializer is not a subclass of {@link OapiPartSerializer}.
* @return This object (for method chaining).
* @throws RestCallException
*/
- public RestCall query(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
- if (partSerializer == null)
- partSerializer = client.getPartSerializer();
+ public RestCall query(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
+ if (serializer == null)
+ serializer = client.getPartSerializer();
if (! ("*".equals(name) || isEmpty(name))) {
if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty))
- uriBuilder.addParameter(name, partSerializer.serialize(HttpPartType.QUERY, value));
+ uriBuilder.addParameter(name, serializer.serialize(HttpPartType.QUERY, schema, value));
} else if (value instanceof NameValuePairs) {
for (NameValuePair p : (NameValuePairs)value)
- query(p.getName(), p.getValue(), skipIfEmpty, SimpleUonPartSerializer.DEFAULT);
+ query(p.getName(), p.getValue(), skipIfEmpty, SimpleUonPartSerializer.DEFAULT, schema);
} else if (value instanceof Map) {
for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- query(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
+ query(p.getKey(), p.getValue(), skipIfEmpty, serializer, schema);
} else if (isBean(value)) {
- return query(name, toBeanMap(value), skipIfEmpty, partSerializer);
+ return query(name, toBeanMap(value), skipIfEmpty, serializer, schema);
} else if (value instanceof Reader) {
try {
uriBuilder.setCustomQuery(read(value));
@@ -221,7 +228,7 @@ public final class RestCall extends BeanSession implements Closeable {
}
} else if (value instanceof CharSequence) {
String s = value.toString();
- if (! isEmpty(s))
+ if (isNotEmpty(s))
uriBuilder.setCustomQuery(s);
} else {
throw new FormattedRuntimeException("Invalid name ''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value));
@@ -238,7 +245,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall query(String name, Object value) throws RestCallException {
- return query(name, value, false, null);
+ return query(name, value, false, null, null);
}
/**
@@ -264,7 +271,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall queryIfNE(String name, Object value) throws RestCallException {
- return query(name, value, true, null);
+ return query(name, value, true, null, null);
}
/**
@@ -278,7 +285,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall queryIfNE(Map<String,Object> params) throws RestCallException {
- return query(null, params, true, null);
+ return query(null, params, true, null, null);
}
/**
@@ -302,29 +309,34 @@ public final class RestCall extends BeanSession implements Closeable {
* The parameter value converted to a string using UON notation.
* Can also be {@link Map}, {@link NameValuePairs}, or bean if the name is null/blank/*.
* @param skipIfEmpty Don't add the pair if the value is empty.
- * @param partSerializer
- * The part serializer to use to convert the value to a string.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
* If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part serializer is not a subclass of {@link OapiPartSerializer}.
* @return This object (for method chaining).
* @throws RestCallException
*/
- public RestCall formData(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
+ public RestCall formData(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
if (formData == null)
formData = new NameValuePairs();
- if (partSerializer == null)
- partSerializer = client.getPartSerializer();
+ if (serializer == null)
+ serializer = client.getPartSerializer();
if (! ("*".equals(name) || isEmpty(name))) {
if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty))
- formData.add(new SerializedNameValuePair(name, value, partSerializer));
+ formData.add(new SerializedNameValuePair(name, value, serializer, schema));
} else if (value instanceof NameValuePairs) {
for (NameValuePair p : (NameValuePairs)value)
if (p.getValue() != null && ! (isEmpty(p.getValue()) && skipIfEmpty))
formData.add(p);
} else if (value instanceof Map) {
for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- formData(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
+ formData(p.getKey(), p.getValue(), skipIfEmpty, serializer, schema);
} else if (isBean(value)) {
- return formData(name, toBeanMap(value), skipIfEmpty, partSerializer);
+ return formData(name, toBeanMap(value), skipIfEmpty, serializer, schema);
} else if (value instanceof Reader) {
contentType("application/x-www-form-urlencoded");
input(value);
@@ -352,7 +364,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}.
*/
public RestCall formData(String name, Object value) throws RestCallException {
- return formData(name, value, false, null);
+ return formData(name, value, false, null, null);
}
/**
@@ -389,7 +401,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall formDataIfNE(String name, Object value) throws RestCallException {
- return formData(name, value, true, null);
+ return formData(name, value, true, null, null);
}
/**
@@ -403,7 +415,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall formDataIfNE(Map<String,Object> params) throws RestCallException {
- return formData(null, params, true, null);
+ return formData(null, params, true, null, null);
}
/**
@@ -411,30 +423,35 @@ public final class RestCall extends BeanSession implements Closeable {
*
* @param name The path variable name.
* @param value The replacement value.
- * @param partSerializer
- * The part serializer to use to convert the value to a string.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
* If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part serializer is not a subclass of {@link OapiPartSerializer}.
* @return This object (for method chaining).
* @throws RestCallException If variable could not be found in path.
*/
- public RestCall path(String name, Object value, HttpPartSerializer partSerializer) throws RestCallException {
+ public RestCall path(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
String path = uriBuilder.getPath();
- if (partSerializer == null)
- partSerializer = client.getPartSerializer();
+ if (serializer == null)
+ serializer = client.getPartSerializer();
if (! ("*".equals(name) || isEmpty(name))) {
String var = "{" + name + "}";
if (path.indexOf(var) == -1)
throw new RestCallException("Path variable {"+name+"} was not found in path.");
- String newPath = path.replace(var, partSerializer.serialize(HttpPartType.PATH, value));
+ String newPath = path.replace(var, serializer.serialize(HttpPartType.PATH, schema, value));
uriBuilder.setPath(newPath);
} else if (value instanceof NameValuePairs) {
for (NameValuePair p : (NameValuePairs)value)
- path(p.getName(), p.getValue(), partSerializer);
+ path(p.getName(), p.getValue(), serializer, schema);
} else if (value instanceof Map) {
for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- path(p.getKey(), p.getValue(), partSerializer);
+ path(p.getKey(), p.getValue(), serializer, schema);
} else if (isBean(value)) {
- return path(name, toBeanMap(value), partSerializer);
+ return path(name, toBeanMap(value), serializer, schema);
} else if (value != null) {
throw new FormattedRuntimeException("Invalid name ''{0}'' passed to path(name,value) for data type ''{1}''", name, getReadableClassNameForObject(value));
}
@@ -450,7 +467,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException If variable could not be found in path.
*/
public RestCall path(String name, Object value) throws RestCallException {
- return path(name, value, null);
+ return path(name, value, null, null);
}
/**
@@ -548,26 +565,31 @@ public final class RestCall extends BeanSession implements Closeable {
* The name can be null/empty if the value is a {@link Map}.
* @param value The header value.
* @param skipIfEmpty Don't add the header if the name is null/empty.
- * @param partSerializer
- * The part serializer to use to convert the value to a string.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
* If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part serializer is not a subclass of {@link OapiPartSerializer}.
* @return This object (for method chaining).
* @throws RestCallException
*/
- public RestCall header(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
- if (partSerializer == null)
- partSerializer = client.getPartSerializer();
+ public RestCall header(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
+ if (serializer == null)
+ serializer = client.getPartSerializer();
if (! ("*".equals(name) || isEmpty(name))) {
if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty))
- request.setHeader(name, partSerializer.serialize(HttpPartType.HEADER, value));
+ request.setHeader(name, serializer.serialize(HttpPartType.HEADER, schema, value));
} else if (value instanceof NameValuePairs) {
for (NameValuePair p : (NameValuePairs)value)
- header(p.getName(), p.getValue(), skipIfEmpty, SimpleUonPartSerializer.DEFAULT);
+ header(p.getName(), p.getValue(), skipIfEmpty, SimpleUonPartSerializer.DEFAULT, schema);
} else if (value instanceof Map) {
for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- header(p.getKey(), p.getValue(), skipIfEmpty, partSerializer);
+ header(p.getKey(), p.getValue(), skipIfEmpty, serializer, schema);
} else if (isBean(value)) {
- return header(name, toBeanMap(value), skipIfEmpty, partSerializer);
+ return header(name, toBeanMap(value), skipIfEmpty, serializer, schema);
} else {
throw new FormattedRuntimeException("Invalid name ''{0}'' passed to header(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value));
}
@@ -586,7 +608,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall header(String name, Object value) throws RestCallException {
- return header(name, value, false, null);
+ return header(name, value, false, null, null);
}
/**
@@ -597,7 +619,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall headers(Map<String,Object> values) throws RestCallException {
- return header(null, values, false, null);
+ return header(null, values, false, null, null);
}
/**
@@ -614,7 +636,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall headerIfNE(String name, Object value) throws RestCallException {
- return header(name, value, true, null);
+ return header(name, value, true, null, null);
}
/**
@@ -628,7 +650,7 @@ public final class RestCall extends BeanSession implements Closeable {
* @throws RestCallException
*/
public RestCall headersIfNE(Map<String,Object> values) throws RestCallException {
- return header(null, values, true, null);
+ return header(null, values, true, null, null);
}
/**
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 7ea8f29..4ec7076 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -30,6 +30,7 @@ import org.apache.http.entity.*;
import org.apache.http.impl.client.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
@@ -1014,16 +1015,16 @@ public class RestClient extends BeanContext implements Closeable {
rc.serializer(serializer).parser(parser);
for (RemoteMethodArg a : rmm.getPathArgs())
- rc.path(a.name, args[a.index], a.serializer);
+ rc.path(a.name, args[a.index], a.serializer, null);
for (RemoteMethodArg a : rmm.getQueryArgs())
- rc.query(a.name, args[a.index], a.skipIfNE, a.serializer);
+ rc.query(a.name, args[a.index], a.skipIfNE, a.serializer, null);
for (RemoteMethodArg a : rmm.getFormDataArgs())
- rc.formData(a.name, args[a.index], a.skipIfNE, a.serializer);
+ rc.formData(a.name, args[a.index], a.skipIfNE, a.serializer, null);
for (RemoteMethodArg a : rmm.getHeaderArgs())
- rc.header(a.name, args[a.index], a.skipIfNE, a.serializer);
+ rc.header(a.name, args[a.index], a.skipIfNE, a.serializer, null);
if (rmm.getBodyArg() != null)
rc.input(args[rmm.getBodyArg()]);
@@ -1039,32 +1040,32 @@ public class RestClient extends BeanContext implements Closeable {
Path p = pMeta.getAnnotation(Path.class);
if (p != null)
- rc.path(getName(p.name(), p.value(), pMeta), val, getPartSerializer(p.serializer(), rma.serializer));
+ rc.path(getName(p.name(), p.value(), pMeta), val, getPartSerializer(p.serializer(), rma.serializer), null);
if (val != null) {
Query q1 = pMeta.getAnnotation(Query.class);
if (q1 != null)
- rc.query(getName(q1.name(), q1.value(), pMeta), val, q1.skipIfEmpty(), getPartSerializer(q1.serializer(), rma.serializer));
+ rc.query(getName(q1.name(), q1.value(), pMeta), val, q1.skipIfEmpty(), getPartSerializer(q1.serializer(), rma.serializer), null);
QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class);
if (q2 != null)
- rc.query(getName(q2.name(), q2.value(), pMeta), val, true, getPartSerializer(q2.serializer(), rma.serializer));
+ rc.query(getName(q2.name(), q2.value(), pMeta), val, true, getPartSerializer(q2.serializer(), rma.serializer), null);
FormData f1 = pMeta.getAnnotation(FormData.class);
if (f1 != null)
- rc.formData(getName(f1.name(), f1.value(), pMeta), val, f1.skipIfEmpty(), getPartSerializer(f1.serializer(), rma.serializer));
+ rc.formData(getName(f1.name(), f1.value(), pMeta), val, f1.skipIfEmpty(), getPartSerializer(f1.serializer(), rma.serializer), null);
FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class);
if (f2 != null)
- rc.formData(getName(f2.name(), f2.value(), pMeta), val, true, getPartSerializer(f2.serializer(), rma.serializer));
+ rc.formData(getName(f2.name(), f2.value(), pMeta), val, true, getPartSerializer(f2.serializer(), rma.serializer), null);
org.apache.juneau.remoteable.Header h1 = pMeta.getAnnotation(org.apache.juneau.remoteable.Header.class);
if (h1 != null)
- rc.header(getName(h1.name(), h1.value(), pMeta), val, h1.skipIfEmpty(), getPartSerializer(h1.serializer(), rma.serializer));
+ rc.header(getName(h1.name(), h1.value(), pMeta), val, h1.skipIfEmpty(), getPartSerializer(h1.serializer(), rma.serializer), null);
HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class);
if (h2 != null)
- rc.header(getName(h2.name(), h2.value(), pMeta), val, true, getPartSerializer(h2.serializer(), rma.serializer));
+ rc.header(getName(h2.name(), h2.value(), pMeta), val, true, getPartSerializer(h2.serializer(), rma.serializer), null);
}
}
}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
index a0bdff8..84b9fc4 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
@@ -48,6 +48,7 @@ import org.apache.juneau.*;
import org.apache.juneau.html.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.msgpack.*;
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
index decd32b..f380302 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
@@ -14,6 +14,7 @@ package org.apache.juneau.rest.client;
import org.apache.http.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -32,18 +33,26 @@ public final class SerializedNameValuePair implements NameValuePair {
private String name;
private Object value;
private HttpPartSerializer serializer;
+ private HttpPartSchema schema;
/**
* Constructor.
*
* @param name The parameter name.
* @param value The POJO to serialize to the parameter value.
- * @param serializer The serializer to use to convert the value to a string.
+ * @param serializer
+ * The serializer to use for serializing the value to a string value.
+ * @param schema
+ * The schema object that defines the format of the output.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part serializer is not a subclass of {@link OapiPartSerializer}.
*/
- public SerializedNameValuePair(String name, Object value, HttpPartSerializer serializer) {
+ public SerializedNameValuePair(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema) {
this.name = name;
this.value = value;
this.serializer = serializer;
+ this.schema = schema;
}
@Override /* NameValuePair */
@@ -53,6 +62,6 @@ public final class SerializedNameValuePair implements NameValuePair {
@Override /* NameValuePair */
public String getValue() {
- return serializer.serialize(HttpPartType.FORMDATA, value);
+ return serializer.serialize(HttpPartType.FORMDATA, schema, value);
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
index c377a4a..39009ae 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
@@ -761,9 +761,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
if (s.isEmpty())
return null;
s = vs.resolve(s.trim());
- if (! isObjectList(s, true))
- return new ObjectList(Arrays.asList(StringUtils.split(s, ',')));
- return new ObjectList(s);
+ return StringUtils.parseListOrCdl(s);
} catch (ParseException e) {
throw new SwaggerException(e, "Malformed swagger JSON array encountered in "+location+".", locationArgs);
}
@@ -900,7 +898,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
boolean isOk = "ok".equals(in), isBody = "body".equals(in);
String sex = example.toString();
- if (RestUtils.isJson(sex)) {
+ if (isJson(sex)) {
example = JsonParser.DEFAULT.parse(JsonSerializer.DEFAULT.serialize(example), type);
} else {
ClassMeta<?> cm = js.getClassMeta(type);
@@ -935,7 +933,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
}
} else {
String paramName = piri.getString("name");
- String s = sm.partSerializer.serialize(HttpPartType.valueOf(in.toUpperCase()), example);
+ String s = sm.partSerializer.serialize(HttpPartType.valueOf(in.toUpperCase()), null, example);
if ("query".equals(in))
s = "?" + urlEncodeLax(paramName) + "=" + urlEncodeLax(s);
else if ("formData".equals(in))
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
index 5ab2582..1ec7790 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
@@ -24,6 +24,7 @@ import javax.servlet.*;
import org.apache.juneau.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.http.*;
+import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.exception.*;
@@ -51,6 +52,8 @@ public class RequestBody {
private int contentLength = 0;
private MediaType mediaType;
private Parser parser;
+ private HttpPartParser partParser;
+ private HttpPartSchema schema;
RequestBody(RestRequest req) {
this.req = req;
@@ -88,6 +91,16 @@ public class RequestBody {
return this;
}
+ RequestBody partParser(HttpPartParser partParser) {
+ this.partParser = partParser;
+ return this;
+ }
+
+ RequestBody schema(HttpPartSchema schema) {
+ this.schema = schema;
+ return this;
+ }
+
boolean isLoaded() {
return body != null;
}
@@ -178,7 +191,7 @@ public class RequestBody {
* for the specified type.
*/
public <T> T asType(Class<T> type) throws IOException, ParseException {
- return parse(beanSession.getClassMeta(type));
+ return parse(getClassMeta(type));
}
/**
@@ -223,7 +236,7 @@ public class RequestBody {
* @return The input parsed to a POJO.
*/
public <T> T asType(Type type, Type...args) {
- return (T)parse(beanSession.getClassMeta(type, args));
+ return parse(this.<T>getClassMeta(type, args));
}
/**
@@ -417,7 +430,10 @@ public class RequestBody {
req.getProperties().append("mediaType", mediaType).append("characterEncoding", req.getCharacterEncoding());
ParserSession session = p.createSession(new ParserSessionArgs(req.getProperties(), req.getJavaMethod(), locale, timeZone, mediaType, req.getContext().getResource()));
try (Closeable in = session.isReaderParser() ? getUnbufferedReader() : getInputStream()) {
- return session.parse(in, cm);
+ T o = session.parse(in, cm);
+ if (schema != null)
+ schema.validateOutput(o, cm.getBeanContext());
+ return o;
}
} catch (ParseException e) {
throw new BadRequest(e,
@@ -434,8 +450,21 @@ public class RequestBody {
return cm.getInputStreamTransform().transform(getInputStream());
MediaType mt = getMediaType();
- if ((isEmpty(mt) || mt.toString().startsWith("text/plain")) && cm.hasStringTransform())
- return cm.getStringTransform().transform(asString());
+ if ((isEmpty(mt) || mt.toString().startsWith("text/plain"))) {
+ if (partParser != null) {
+ try {
+ String in = asString();
+ return partParser.parse(HttpPartType.BODY, schema, isEmpty(in) ? null : in, cm);
+ } catch (ParseException e) {
+ throw new BadRequest(e,
+ "Could not convert request body content to class type ''{0}'' using parser ''{1}''.",
+ cm, partParser.getClass().getName()
+ );
+ }
+ } else if (cm.hasStringTransform()) {
+ return cm.getStringTransform().transform(asString());
+ }
+ }
throw new UnsupportedMediaType(
"Unsupported media-type in request header ''Content-Type'': ''{0}''\n\tSupported media-types: {1}",
@@ -453,7 +482,7 @@ public class RequestBody {
private Encoder getEncoder() throws UnsupportedMediaType {
if (encoder == null) {
String ce = req.getHeader("content-encoding");
- if (! isEmpty(ce)) {
+ if (isNotEmpty(ce)) {
ce = ce.trim();
encoder = encoders.getEncoder(ce);
if (encoder == null)
@@ -481,4 +510,17 @@ public class RequestBody {
public int getContentLength() {
return contentLength == 0 ? req.getRawContentLength() : contentLength;
}
+
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
+ return beanSession.getClassMeta(type, args);
+ }
+
+ private <T> ClassMeta<T> getClassMeta(Class<T> type) {
+ return beanSession.getClassMeta(type);
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
index 242b60d..5666290 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
@@ -22,6 +22,7 @@ import javax.servlet.http.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
@@ -246,7 +247,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, Class<T> type) throws ParseException {
- return get(null, name, type);
+ return getInner(null, null, name, null, getClassMeta(type));
}
/**
@@ -255,14 +256,19 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type The class type to convert the parameter value to.
* @param <T> The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
- return parse(parser, name, getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, null, getClassMeta(type));
}
/**
@@ -276,7 +282,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, T def, Class<T> type) throws ParseException {
- return get(null, name, def, type);
+ return getInner(null, null, name, def, getClassMeta(type));
}
/**
@@ -285,6 +291,11 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param def The default value if the parameter was not specified or is <jk>null</jk>.
* @param type The class type to convert the parameter value to.
@@ -292,8 +303,8 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, T def, Class<T> type) throws ParseException {
- return parse(parser, name, def, getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, def, getClassMeta(type));
}
/**
@@ -309,7 +320,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T getAll(String name, Class<T> type) throws ParseException {
- return getAll(null, name, type);
+ return getAllInner(null, null, name, null, getClassMeta(type));
}
/**
@@ -321,13 +332,18 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T getAll(HttpPartParser parser, String name, Class<T> type) throws ParseException {
- return parseAll(parser, name, getClassMeta(type));
+ public <T> T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws ParseException {
+ return getAllInner(parser, schema, name, null, getClassMeta(type));
}
/**
@@ -376,7 +392,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, Type type, Type...args) throws ParseException {
- return get(null, name, type, args);
+ return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
}
/**
@@ -385,6 +401,11 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type
* The type of object to create.
@@ -396,8 +417,56 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- return (T)parse(parser, name, getClassMeta(type, args));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except returns a default value if not found.
+ *
+ * @param name The parameter name.
+ * @param type
+ * The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args
+ * The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, T def, Type type, Type...args) throws ParseException {
+ return getInner(null, null, name, def, this.<T>getClassMeta(type, args));
+ }
+
+ /**
+ * Same as {@link #get(String, Object, Type, Type...)} but allows you to override the part parser.
+ *
+ * @param parser
+ * The parser to use for parsing the string value.
+ * <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
+ * @param name The parameter name.
+ * @param type
+ * The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args
+ * The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, def, this.<T>getClassMeta(type, args));
}
/**
@@ -419,7 +488,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T getAll(String name, Type type, Type...args) throws ParseException {
- return getAll(null, name, type, args);
+ return getAllInner(null, null, name, null, this.<T>getClassMeta(type, args));
}
/**
@@ -428,6 +497,11 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type
* The type of object to create.
@@ -439,45 +513,34 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T getAll(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- return (T)parseAll(parser, name, getClassMeta(type, args));
+ public <T> T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getAllInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
}
-
/* Workhorse method */
- <T> T parse(HttpPartParser parser, String name, T def, ClassMeta<T> cm) throws ParseException {
- String val = getString(name);
- if (val == null)
- return def;
- return parseValue(parser, val, cm);
- }
-
- /* Workhorse method */
- <T> T parse(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
- String val = getString(name);
- if (cm.isPrimitive() && (val == null || val.isEmpty()))
- return cm.getPrimitiveDefault();
- return parseValue(parser, val, cm);
+ private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
+ T t = parse(parser, schema, getString(name), cm);
+ return (t == null ? def : t);
}
/* Workhorse method */
@SuppressWarnings("rawtypes")
- <T> T parseAll(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
+ <T> T getAllInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
String[] p = get(name);
- if (p == null)
- return null;
- if (parser == null)
- parser = this.parser;
+ if (p == null)
+ return def;
+ if (schema == null)
+ schema = HttpPartSchema.DEFAULT;
if (cm.isArray()) {
List c = new ArrayList();
for (int i = 0; i < p.length; i++)
- c.add(parseValue(parser, p[i], cm.getElementType()));
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
return (T)toArray(c, cm.getElementType().getInnerClass());
} else if (cm.isCollection()) {
try {
Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
for (int i = 0; i < p.length; i++)
- c.add(parseValue(parser, p[i], cm.getElementType()));
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
return (T)c;
} catch (ParseException e) {
throw e;
@@ -489,14 +552,10 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
throw new ParseException("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array.");
}
- private <T> T parseValue(HttpPartParser parser, String val, ClassMeta<T> c) throws ParseException {
- try {
- if (parser == null)
- parser = this.parser;
- return parser.parse(HttpPartType.FORMDATA, val, c);
- } catch (Exception e) {
- throw new ParseException(e);
- }
+ private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws ParseException {
+ if (parser == null)
+ parser = this.parser;
+ return parser.parse(HttpPartType.FORMDATA, schema, val, c);
}
/**
@@ -515,16 +574,20 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
return JsonSerializer.DEFAULT_LAX.toString(m);
}
- private ClassMeta<?> getClassMeta(Type type, Type...args) {
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
return beanSession.getClassMeta(type, args);
}
private <T> ClassMeta<T> getClassMeta(Class<T> type) {
return beanSession.getClassMeta(type);
}
-
- @Override /* Object */
- public String toString() {
- return toString(false);
- }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
index 44f780e..eec09cc 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -22,6 +22,7 @@ import org.apache.juneau.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.Date;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
@@ -252,7 +253,7 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, Class<T> type) throws ParseException {
- return get(parser, name, null, type);
+ return getInner(null, null, name, null, getClassMeta(type));
}
/**
@@ -261,14 +262,19 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @param parser
* The parser to use for parsing the string header.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The HTTP header name.
* @param type The class type to convert the header value to.
* @param <T> The class type to convert the header value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
- return get(parser, name, null, type);
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, null, getClassMeta(type));
}
/**
@@ -282,7 +288,7 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, T def, Class<T> type) throws ParseException {
- return get(parser, name, def, type);
+ return getInner(null, null, name, def, getClassMeta(type));
}
/**
@@ -291,6 +297,11 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @param parser
* The parser to use for parsing the string header.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The HTTP header name.
* @param def The default value if the header was not specified or is <jk>null</jk>.
* @param type The class type to convert the header value to.
@@ -298,13 +309,8 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, T def, Class<T> type) throws ParseException {
- String s = getString(name);
- if (s == null)
- return def;
- if (parser == null)
- parser = this.parser;
- return parser.parse(HttpPartType.HEADER, s, getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, def, getClassMeta(type));
}
/**
@@ -347,7 +353,7 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @throws ParseException If the header could not be converted to the specified type.
*/
public <T> T get(String name, Type type, Type...args) throws ParseException {
- return get(null, name, type, args);
+ return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
}
/**
@@ -356,6 +362,11 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @param parser
* The parser to use for parsing the string header.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name
* The HTTP header name.
* @param type
@@ -369,14 +380,21 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException If the header could not be converted to the specified type.
*/
- @SuppressWarnings("unchecked")
- public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- String s = getString(name);
- if (s == null)
- return null;
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
+ }
+
+ /* Workhorse method */
+ private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
+ T t = parse(parser, schema, getString(name), cm);
+ return (t == null ? def : t);
+ }
+
+ /* Workhorse method */
+ private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> cm) throws ParseException {
if (parser == null)
parser = this.parser;
- return (T)parser.parse(HttpPartType.HEADER, s, getClassMeta(type, args));
+ return parser.parse(HttpPartType.HEADER, schema, val, cm);
}
/**
@@ -916,16 +934,20 @@ public class RequestHeaders extends TreeMap<String,String[]> {
return JsonSerializer.DEFAULT_LAX.toString(m);
}
- private ClassMeta<?> getClassMeta(Type type, Type...args) {
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
return beanSession.getClassMeta(type, args);
}
private <T> ClassMeta<T> getClassMeta(Class<T> type) {
return beanSession.getClassMeta(type);
}
-
- @Override /* Object */
- public String toString() {
- return toString(false);
- }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
index a32f216..6974649 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
@@ -19,6 +19,7 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.parser.*;
/**
@@ -32,7 +33,6 @@ import org.apache.juneau.parser.*;
* <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-rest-server.RequestPathMatch">Overview > juneau-rest-server > RequestPathMatch</a>
* </ul>
*/
-@SuppressWarnings("unchecked")
public class RequestPathMatch extends TreeMap<String,String> {
private static final long serialVersionUID = 1L;
@@ -82,7 +82,7 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @throws ParseException
*/
public String getString(String name) throws ParseException {
- return parse(parser, name, beanSession.string());
+ return getInner(parser, null, name, null, beanSession.string());
}
/**
@@ -93,7 +93,7 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @throws ParseException
*/
public int getInt(String name) throws ParseException {
- return parse(parser, name, beanSession.getClassMeta(int.class));
+ return getInner(parser, null, name, null, getClassMeta(int.class));
}
/**
@@ -104,7 +104,7 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @throws ParseException
*/
public boolean getBoolean(String name) throws ParseException {
- return parse(parser, name, beanSession.getClassMeta(boolean.class));
+ return getInner(null, null, name, null, getClassMeta(boolean.class));
}
/**
@@ -140,7 +140,7 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @throws ParseException
*/
public <T> T get(String name, Class<T> type) throws ParseException {
- return get(parser, name, type);
+ return getInner(null, null, name, null, this.<T>getClassMeta(type));
}
/**
@@ -149,14 +149,19 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The attribute name.
* @param type The class type to convert the attribute value to.
* @param <T> The class type to convert the attribute value to.
* @return The attribute value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
- return parse(parser, name, beanSession.getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, null, this.<T>getClassMeta(type));
}
/**
@@ -209,7 +214,7 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @throws ParseException
*/
public <T> T get(String name, Type type, Type...args) throws ParseException {
- return get(parser, name, type, args);
+ return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
}
/**
@@ -218,6 +223,11 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The attribute name.
* @param type
* The type of object to create.
@@ -230,22 +240,21 @@ public class RequestPathMatch extends TreeMap<String,String> {
* @return The attribute value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- return (T)parse(parser, name, beanSession.getClassMeta(type, args));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
}
-
-
+
+ /* Workhorse method */
+ private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
+ T t = parse(parser, schema, get(name), cm);
+ return (t == null ? def : t);
+ }
+
/* Workhorse method */
- <T> T parse(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
+ private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> cm) throws ParseException {
if (parser == null)
parser = this.parser;
- Object attr = get(name);
- T t = null;
- if (attr != null)
- t = parser.parse(HttpPartType.PATH, attr.toString(), cm);
- if (t == null && cm.isPrimitive())
- return cm.getPrimitiveDefault();
- return t;
+ return parser.parse(HttpPartType.PATH, schema, val, cm);
}
/**
@@ -320,4 +329,16 @@ public class RequestPathMatch extends TreeMap<String,String> {
public String getPattern() {
return pattern;
}
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
+ return beanSession.getClassMeta(type, args);
+ }
+
+ private <T> ClassMeta<T> getClassMeta(Class<T> type) {
+ return beanSession.getClassMeta(type);
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
index ff949fa..5e358eb 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
@@ -22,6 +22,7 @@ import javax.servlet.http.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
@@ -245,7 +246,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, Class<T> type) throws ParseException {
- return get(null, name, type);
+ return getInner(null, null, name, null, getClassMeta(type));
}
/**
@@ -254,14 +255,19 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type The class type to convert the parameter value to.
* @param <T> The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
- return get(parser, name, getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, null, getClassMeta(type));
}
/**
@@ -275,7 +281,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, T def, Class<T> type) throws ParseException {
- return get(null, name, def, type);
+ return getInner(null, null, name, def, getClassMeta(type));
}
/**
@@ -284,6 +290,11 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param def The default value if the parameter was not specified or is <jk>null</jk>.
* @param type The class type to convert the parameter value to.
@@ -291,8 +302,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, T def, Class<T> type) throws ParseException {
- return get(parser, name, def, getClassMeta(type));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws ParseException {
+ return getInner(parser, schema, name, def, getClassMeta(type));
}
/**
@@ -342,7 +353,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T get(String name, Type type, Type...args) throws ParseException {
- return get((HttpPartParser)null, name, type, args);
+ return getInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -351,7 +362,11 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
- *
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type
* The type of object to create.
@@ -364,8 +379,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- return (T)parse(parser, name, getClassMeta(type, args));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -384,8 +399,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(String name, Object def, Type type, Type...args) throws ParseException {
- return get(null, name, def, type, args);
+ public <T> T get(String name, T def, Type type, Type...args) throws ParseException {
+ return getInner(null, null, name, def, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -394,6 +409,11 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The parameter name.
* @param type
* The type of object to create.
@@ -407,8 +427,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T get(HttpPartParser parser, String name, Object def, Type type, Type...args) throws ParseException {
- return (T)parse(parser, name, def, getClassMeta(type, args));
+ public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws ParseException {
+ return getInner(parser, schema, name, def, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -425,7 +445,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T getAll(String name, Class<T> c) throws ParseException {
- return getAll(name, beanSession.getClassMeta(c));
+ return getAllInner(null, null, name, null, beanSession.getClassMeta(c));
}
/**
@@ -448,7 +468,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws ParseException
*/
public <T> T getAll(String name, Type type, Type...args) throws ParseException {
- return getAll(null, name, type, args);
+ return getAllInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -457,6 +477,11 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @param parser
* The parser to use for parsing the string value.
* <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Ignored if the part parser is not a subclass of {@link OapiPartParser}.
* @param name The query parameter name.
* @param type
* The type of object to create.
@@ -469,8 +494,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @return The query parameter value converted to the specified class type.
* @throws ParseException
*/
- public <T> T getAll(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
- return (T)parseAll(parser, name, getClassMeta(type, args));
+ public <T> T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws ParseException {
+ return getAllInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -552,37 +577,29 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
}
/* Workhorse method */
- private <T> T parse(HttpPartParser parser, String name, Object def, ClassMeta<T> cm) throws ParseException {
- String val = getString(name);
- if (val == null)
- return (T)def;
- return parseValue(parser, val, cm);
- }
-
- /* Workhorse method */
- private <T> T parse(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
- String val = getString(name);
- if (cm.isPrimitive() && (val == null || val.isEmpty()))
- return cm.getPrimitiveDefault();
- return parseValue(parser, val, cm);
+ private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
+ T t = parse(parser, schema, getString(name), cm);
+ return (t == null ? def : t);
}
/* Workhorse method */
@SuppressWarnings("rawtypes")
- private <T> T parseAll(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
+ private <T> T getAllInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws ParseException {
String[] p = get(name);
if (p == null)
- return null;
+ return def;
+ if (schema == null)
+ schema = HttpPartSchema.DEFAULT;
if (cm.isArray()) {
List c = new ArrayList();
for (int i = 0; i < p.length; i++)
- c.add(parseValue(parser, p[i], cm.getElementType()));
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
return (T)toArray(c, cm.getElementType().getInnerClass());
} else if (cm.isCollection()) {
try {
Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
for (int i = 0; i < p.length; i++)
- c.add(parseValue(parser, p[i], cm.getElementType()));
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
return (T)c;
} catch (ParseException e) {
throw e;
@@ -594,10 +611,10 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
throw new ParseException("Invalid call to getQueryParameters(String, ClassMeta). Class type must be a Collection or array.");
}
- private <T> T parseValue(HttpPartParser parser, String val, ClassMeta<T> c) throws ParseException {
+ private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws ParseException {
if (parser == null)
parser = this.parser;
- return parser.parse(HttpPartType.QUERY, val, c);
+ return parser.parse(HttpPartType.QUERY, schema, val, c);
}
/**
@@ -635,16 +652,20 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
return sb.toString();
}
- private ClassMeta<?> getClassMeta(Type type, Type...args) {
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
return beanSession.getClassMeta(type, args);
}
private <T> ClassMeta<T> getClassMeta(Class<T> type) {
return beanSession.getClassMeta(type);
}
-
- @Override /* Object */
- public String toString() {
- return toString(false);
- }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 5a1c34a..2e58d7c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -17,7 +17,6 @@ import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.IOUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
-import static org.apache.juneau.internal.ObjectUtils.*;
import java.io.*;
import java.lang.annotation.*;
@@ -39,6 +38,8 @@ import org.apache.juneau.html.*;
import org.apache.juneau.htmlschema.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.*;
@@ -1735,7 +1736,7 @@ public final class RestContext extends BeanContext {
* <ul>
* <li><b>Name:</b> <js>"RestContext.partParser.o"</js>
* <li><b>Data type:</b> <code>{@link HttpPartParser} | Class<? <jk>extends</jk> {@link HttpPartParser}></code>
- * <li><b>Default:</b> {@link UonPartParser}
+ * <li><b>Default:</b> {@link OapiPartSerializer}
* <li><b>Session-overridable:</b> <jk>false</jk>
* <li><b>Annotations:</b>
* <ul>
@@ -1753,7 +1754,7 @@ public final class RestContext extends BeanContext {
* Specifies the {@link HttpPartParser} to use for parsing headers, query/form parameters, and URI parts.
*
* <p>
- * The default value is {@link UonPartParser} which allows for both plain-text and URL-Encoded-Object-Notation values.
+ * The default value is {@link OapiPartParser} which allows for both plain-text and URL-Encoded-Object-Notation values.
* <br>If your parts contain text that can be confused with UON (e.g. <js>"(foo)"</js>), you can switch to
* {@link SimplePartParser} which treats everything as plain text.
*
@@ -1803,7 +1804,7 @@ public final class RestContext extends BeanContext {
* <ul>
* <li><b>Name:</b> <js>"RestContext.partSerializer.o"</js>
* <li><b>Data type:</b> <code>{@link HttpPartSerializer} | Class<? <jk>extends</jk> {@link HttpPartSerializer}></code>
- * <li><b>Default:</b> {@link SimpleUonPartSerializer}
+ * <li><b>Default:</b> {@link OapiPartSerializer}
* <li><b>Session-overridable:</b> <jk>false</jk>
* <li><b>Annotations:</b>
* <ul>
@@ -1821,12 +1822,14 @@ public final class RestContext extends BeanContext {
* Specifies the {@link HttpPartSerializer} to use for serializing headers, query/form parameters, and URI parts.
*
* <p>
- * The default value is {@link SimpleUonPartSerializer} which serializes UON notation for beans and maps, and
+ * The default value is {@link OapiPartSerializer} which serializes based on OpenAPI rules, but defaults to UON notation for beans and maps, and
* plain text for everything else.
* <br>Other options include:
* <ul>
* <li class='jc'>{@link SimplePartSerializer} - Always serializes to plain text.
* <li class='jc'>{@link UonPartSerializer} - Always serializers to UON.
+ * <li class='jc'>{@link SimpleUonPartSerializer} - Serializes to UON notation for beans and maps, and
+ * plain text for everything else..
* </ul>
*
* <h5 class='section'>Example:</h5>
@@ -2919,8 +2922,8 @@ public final class RestContext extends BeanContext {
properties = builder.properties;
serializers = SerializerGroup.create().append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], true, resource, ps)).build();
parsers = ParserGroup.create().append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], true, resource, ps)).build();
- partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, SimpleUonPartSerializer.class, true, resource, ps);
- partParser = getInstanceProperty(REST_partSerializer, HttpPartParser.class, UonPartParser.class, true, resource, ps);
+ partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OapiPartSerializer.class, true, resource, ps);
+ partParser = getInstanceProperty(REST_partSerializer, HttpPartParser.class, OapiPartParser.class, true, resource, ps);
jsonSchemaSerializer = new JsonSchemaSerializer(ps);
encoders = new EncoderGroupBuilder().append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], true, resource, ps)).build();
beanContext = BeanContext.create().apply(ps).build();
@@ -3956,7 +3959,7 @@ public final class RestContext extends BeanContext {
* @return <jk>true</jk> if this resource allows the specified method to be overridden.
*/
public boolean allowMethodParam(String m) {
- return (! isEmpty(m) && (allowedMethodParams.contains(m) || allowedMethodParams.contains("*")));
+ return (isNotEmpty(m) && (allowedMethodParams.contains(m) || allowedMethodParams.contains("*")));
}
/**
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index 4ca2923..51569d8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -33,6 +33,7 @@ import org.apache.juneau.config.vars.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.annotation.*;
@@ -1280,7 +1281,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
*
* @param value
* The new value for this setting.
- * <br>The default is {@link UonPartParser}.
+ * <br>The default is {@link OapiPartParser}.
* @return This object (for method chaining).
*/
public RestContextBuilder partParser(Class<? extends HttpPartParser> value) {
@@ -1300,7 +1301,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
*
* @param value
* The new value for this setting.
- * <br>The default is {@link UonPartParser}.
+ * <br>The default is {@link OapiPartParser}.
* @return This object (for method chaining).
*/
public RestContextBuilder partParser(HttpPartParser value) {
@@ -1320,7 +1321,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
*
* @param value
* The new value for this setting.
- * <br>The default is {@link SimpleUonPartSerializer}.
+ * <br>The default is {@link OapiPartSerializer}.
* @return This object (for method chaining).
*/
public RestContextBuilder partSerializer(Class<? extends HttpPartSerializer> value) {
@@ -1340,7 +1341,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
*
* @param value
* The new value for this setting.
- * <br>The default is {@link SimpleUonPartSerializer}.
+ * <br>The default is {@link OapiPartSerializer}.
* @return This object (for method chaining).
*/
public RestContextBuilder partSerializer(HttpPartSerializer value) {
@@ -1698,7 +1699,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
* @return This object (for method chaining).
*/
public RestContextBuilder staticFiles(Class<?> baseClass, String mappingString) {
- if (! isEmpty(mappingString))
+ if (isNotEmpty(mappingString))
staticFiles(new StaticFileMapping(baseClass, mappingString));
return this;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index bd3956b..dfb194e 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -548,9 +548,13 @@ class RestParamDefaults {
//-------------------------------------------------------------------------------------------------------------------
static final class PathObject extends RestMethodParam {
+ private final HttpPartParser partParser;
+ private final HttpPartSchema schema;
protected PathObject(Method method, Path a, Type type, PropertyStore ps, RestMethodParam existing) {
super(PATH, method, firstNonEmpty(existing == null ? null : existing.name, a.name(), a.value()), type, getMetaData(a, castOrNull(existing, PathObject.class)));
+ this.partParser = a.parser() == HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, a.parser(), true, ps);
+ this.schema = getSchema(metaData, method, "@Path");
}
@Override /* RestMethodParam */
@@ -561,7 +565,7 @@ class RestParamDefaults {
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
- return req.getPathMatch().get(name, type);
+ return req.getPathMatch().get(partParser, schema, name, type);
}
private static final ObjectMap getMetaData(Path a, PathObject existing) {
@@ -573,14 +577,17 @@ class RestParamDefaults {
}
static final class BodyObject extends RestMethodParam {
+ private final HttpPartSchema schema;
protected BodyObject(Method method, Body a, Type type, RestMethodParam existing) {
super(BODY, method, null, type, getMetaData(a, castOrNull(existing, BodyObject.class)));
+ this.schema = getSchema(metaData, method, "@Body");
}
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
- return req.getBody().asType(type);
+ Object o = req.getBody().schema(schema).asType(type);
+ return o;
}
private static final ObjectMap getMetaData(Body a, BodyObject existing) {
@@ -591,10 +598,12 @@ class RestParamDefaults {
static final class HeaderObject extends RestMethodParam {
private final HttpPartParser partParser;
+ private final HttpPartSchema schema;
protected HeaderObject(Method method, Header a, Type type, PropertyStore ps, RestMethodParam existing) {
super(HEADER, method, firstNonEmpty(existing == null ? null : existing.name, a.name(), a.value()), type, getMetaData(a, castOrNull(existing, HeaderObject.class)));
this.partParser = a.parser() == HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, a.parser(), true, ps);
+ this.schema = getSchema(metaData, method, "@Header");
}
@Override /* RestMethodParam */
@@ -605,7 +614,7 @@ class RestParamDefaults {
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
- return req.getHeaders().get(partParser, name, type);
+ return req.getHeaders().get(partParser, schema, name, type);
}
private static ObjectMap getMetaData(Header a, HeaderObject existing) {
@@ -616,10 +625,12 @@ class RestParamDefaults {
static final class ResponseHeaderObject extends RestMethodParam {
final HttpPartSerializer partSerializer;
+ final HttpPartSchema schema;
protected ResponseHeaderObject(Method method, ResponseHeader a, Type type, PropertyStore ps, RestMethodParam existing) {
super(RESPONSE_HEADER, method, firstNonEmpty(existing == null ? null : existing.name, a.name(), a.value()), type, getMetaData(a, castOrNull(existing, ResponseHeaderObject.class)));
this.partSerializer = a.serializer() == HttpPartSerializer.Null.class ? null : ClassUtils.newInstance(HttpPartSerializer.class, a.serializer(), true, ps);
+ this.schema = getSchema(metaData, method, "@Reponse");
}
@Override /* RestMethodParam */
@@ -639,7 +650,7 @@ class RestParamDefaults {
v.listener(new ValueListener() {
@Override
public void onSet(Object newValue) {
- res.setHeader(name, partSerializer.serialize(HttpPartType.HEADER, newValue));
+ res.setHeader(name, partSerializer.serialize(HttpPartType.HEADER, schema, newValue));
}
});
String def = getMetaData().getString("default");
@@ -777,11 +788,13 @@ class RestParamDefaults {
static final class FormDataObject extends RestMethodParam {
private final boolean multiPart;
private final HttpPartParser partParser;
+ private final HttpPartSchema schema;
protected FormDataObject(Method method, FormData a, Type type, PropertyStore ps, RestMethodParam existing) {
super(FORM_DATA, method, firstNonEmpty(existing == null ? null : existing.name, a.name(), a.value()), type, getMetaData(a, castOrNull(existing, FormDataObject.class)));
- this.multiPart = a.multipart();
this.partParser = a.parser() == HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, a.parser(), true, ps);
+ this.schema = getSchema(metaData, method, "@FormData");
+ this.multiPart = schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
}
@Override /* RestMethodParam */
@@ -795,8 +808,8 @@ class RestParamDefaults {
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
if (multiPart)
- return req.getFormData().getAll(partParser, name, type);
- return req.getFormData().get(partParser, name, type);
+ return req.getFormData().getAll(partParser, schema, name, type);
+ return req.getFormData().get(partParser, schema, name, type);
}
private static final ObjectMap getMetaData(FormData a, FormDataObject existing) {
@@ -808,11 +821,13 @@ class RestParamDefaults {
static final class QueryObject extends RestMethodParam {
private final boolean multiPart;
private final HttpPartParser partParser;
+ private final HttpPartSchema schema;
protected QueryObject(Method method, Query a, Type type, PropertyStore ps, RestMethodParam existing) {
super(QUERY, method, firstNonEmpty(existing == null ? null : existing.name, a.name(), a.value()), type, getMetaData(a, castOrNull(existing, QueryObject.class)));
- this.multiPart = a.multipart();
+ this.multiPart = "multi".equals(a.collectionFormat());
this.partParser = a.parser() == HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, a.parser(), true, ps);
+ this.schema = getSchema(metaData, method, "@Query");
}
@Override /* RestMethodParam */
@@ -826,8 +841,8 @@ class RestParamDefaults {
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
if (multiPart)
- return req.getQuery().getAll(partParser, name, type);
- return req.getQuery().get(partParser, name, type);
+ return req.getQuery().getAll(partParser, schema, name, type);
+ return req.getQuery().get(partParser, schema, name, type);
}
private static final ObjectMap getMetaData(Query a, QueryObject existing) {
@@ -842,7 +857,7 @@ class RestParamDefaults {
super(FORM_DATA, method, firstNonEmpty(a.name(), a.value()), type);
if (type != Boolean.class && type != boolean.class)
throw new RestServletException("Use of @HasForm annotation on parameter that is not a boolean on method ''{0}''", method);
- }
+ }
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
@@ -1189,4 +1204,91 @@ class RestParamDefaults {
static final boolean isCollection(Type t) {
return BeanContext.DEFAULT.getClassMeta(t).isCollectionOrArray();
}
+
+ static HttpPartSchema getSchema(ObjectMap md, Method m, String argType) throws InternalServerError {
+ try {
+ return getSchemaBuilder(md).build();
+ } catch (Exception e) {
+ throw new InternalServerError(e, "Invalid {0} argument on class ''{1}'' method ''{2}''", argType, m.getDeclaringClass().getName(), m.getName());
+ }
+ }
+
+ static HttpPartSchema.Builder getSchemaBuilder(ObjectMap md) throws ParseException {
+ ObjectMap md2 = md.getObjectMap("schema", ObjectMap.EMPTY_MAP);
+ HttpPartSchema.Builder b = HttpPartSchema
+ .create()
+ ._default(md.getString("default"))
+ ._default(md2.getString("default"))
+ ._enum(toSet(md.getString("enum")))
+ ._enum(toSet(md2.getString("enum")))
+ .allowEmptyValue(md.getBoolean("allowEmptyValue"))
+ .allowEmptyValue(md2.getBoolean("allowEmptyValue"))
+ .exclusiveMaximum(md.getBoolean("exclusiveMaximum"))
+ .exclusiveMaximum(md2.getBoolean("exclusiveMaximum"))
+ .exclusiveMinimum(md.getBoolean("exclusiveMinimum"))
+ .exclusiveMinimum(md2.getBoolean("exclusiveMinimum"))
+ .required(md.getBoolean("required"))
+ .required(md2.getBoolean("required"))
+ .uniqueItems(md.getBoolean("uniqueItems"))
+ .uniqueItems(md2.getBoolean("uniqueItems"))
+ .collectionFormat(md.getString("collectionFormat"))
+ .collectionFormat(md2.getString("collectionFormat"))
+ .type(md.getString("type"))
+ .type(md2.getString("type"))
+ .format(md.getString("format"))
+ .format(md2.getString("format"))
+ .pattern(md.getString("pattern"))
+ .pattern(md2.getString("pattern"))
+ .maximum(md.get("maximum", Number.class))
+ .maximum(md2.get("maximum", Number.class))
+ .minimum(md.get("minimum", Number.class))
+ .minimum(md2.get("minimum", Number.class))
+ .multipleOf(md.get("multipleOf", Number.class))
+ .multipleOf(md2.get("multipleOf", Number.class))
+ .maxItems(md.get("maxItems", Integer.class))
+ .maxItems(md2.get("maxItems", Integer.class))
+ .maxLength(md.get("maxLength", Integer.class))
+ .maxLength(md2.get("maxLength", Integer.class))
+ .maxProperties(md.get("maxProperties", Integer.class))
+ .maxProperties(md2.get("maxProperties", Integer.class))
+ .minItems(md.get("minItems", Integer.class))
+ .minItems(md2.get("minItems", Integer.class))
+ .minLength(md.get("minLength", Integer.class))
+ .minLength(md2.get("minLength", Integer.class))
+ .minProperties(md.get("minProperties", Integer.class))
+ .minProperties(md2.get("minProperties", Integer.class))
+ ;
+
+ ObjectMap items = md.getObjectMap("items");
+ if (items != null)
+ b.items(getSchemaBuilder(items));
+
+ ObjectMap additionalProperties = md.getObjectMap("additionalProperties");
+ if (additionalProperties != null)
+ b.additionalProperties(getSchemaBuilder(additionalProperties));
+
+ ObjectMap properties = md.getObjectMap("properties");
+ if (properties != null) {
+ for (String name : properties.keySet()) {
+ b.property(name, getSchemaBuilder(md.getObjectMap(name)));
+ }
+ }
+
+ return b;
+ }
+
+ private static Set<String> toSet(String s) {
+ if (isEmpty(s))
+ return null;
+ Set<String> set = new ASet<>();
+ try {
+ for (Object o : StringUtils.parseListOrCdl(s))
+ set.add(o.toString());
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return set;
+ }
+
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index ea1c224..de45749 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -1078,6 +1078,15 @@ public final class RestRequest extends HttpServletRequestWrapper {
*
* @return The part serializer associated with this request.
*/
+ public HttpPartParser getPartParser() {
+ return restJavaMethod.partParser;
+ }
+
+ /**
+ * Returns the part serializer associated with this request.
+ *
+ * @return The part serializer associated with this request.
+ */
public HttpPartSerializer getPartSerializer() {
return restJavaMethod.partSerializer;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index e24cafa..8a22486 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -80,7 +80,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
String passThroughHeaders = req.getHeader("x-response-headers");
if (passThroughHeaders != null) {
HttpPartParser p = context.getPartParser();
- ObjectMap m = p.parse(HttpPartType.HEADER, passThroughHeaders, context.getBeanContext().getClassMeta(ObjectMap.class));
+ ObjectMap m = p.parse(HttpPartType.HEADER, null, passThroughHeaders, context.getBeanContext().getClassMeta(ObjectMap.class));
for (Map.Entry<String,Object> e : m.entrySet())
setHeader(e.getKey(), e.getValue().toString());
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
index 9e33dab..2321d3a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
@@ -23,6 +23,7 @@ import java.util.logging.*;
import org.apache.juneau.*;
import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.*;
+import org.apache.juneau.rest.exception.*;
/**
* REST request body annotation.
@@ -212,8 +213,11 @@ public @interface Body {
* <mk>required</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines whether the body is mandatory. The default value is <jk>false</jk>.
+ * Determines whether the body is mandatory.
*
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
* <h5 class='section'>Examples:</h5>
* <p class='bcode'>
* <jc>// Used on parameter</jc>
@@ -240,7 +244,7 @@ public @interface Body {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String required() default "";
+ boolean required() default false;
//=================================================================================================================
// Attributes specific to in=body
@@ -259,7 +263,7 @@ public @interface Body {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
@@ -280,10 +284,10 @@ public @interface Body {
//=================================================================================================================
/**
- * TODO
+ * A serialized example of the body of a request.
*
* <p>
- * This is the JSON or String representation of an example of the body.
+ * This is the {@link JsonSerializer#DEFAULT_LAX Simple-JSON} or String representation of an example of the body.
*
* <p>
* This value is converted to a POJO and then serialized to all the registered serializers on the REST method to produce examples for all
@@ -367,7 +371,7 @@ public @interface Body {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is any JSON if the object can be converted to a POJO using {@link JsonParser#DEFAULT} or a simple String if the object
+ * The format is any {@link JsonSerializer#DEFAULT_LAX Simple-JSON} if the object can be converted to a POJO using {@link JsonParser#DEFAULT} or a simple String if the object
* can be converted from a String.
* <br>Multiple lines are concatenated with newlines.
* <li>
@@ -378,10 +382,10 @@ public @interface Body {
String[] example() default {};
/**
- * TODO
+ * Serialized examples of the body of a request.
*
* <p>
- * This is a JSON object whose keys are media types and values are string representations of that value.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object whose keys are media types and values are string representations of that value.
*
* <p>
* In general you won't need to populate this value directly since it will automatically be calculated based on the value provided in the {@link #example()} field.
@@ -399,7 +403,7 @@ public @interface Body {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object with string keys (media type) and string values (example for that media type) .
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object with string keys (media type) and string values (example for that media type) .
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <li>
@@ -418,7 +422,7 @@ public @interface Body {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this parameter-info.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this parameter-info.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of the body:
@@ -466,7 +470,9 @@ public @interface Body {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
+ * <li>
+ * Automatic validation is NOT performed on input based on attributes in this value.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Contact.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Contact.java
index af9cfc4..3f9458b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Contact.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Contact.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
+
/**
* Swagger contact annotation.
*
@@ -166,7 +168,7 @@ public @interface Contact {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ExternalDocs.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ExternalDocs.java
index e213bd3..0b45080 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ExternalDocs.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ExternalDocs.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.annotation;
+import org.apache.juneau.json.*;
+
/**
* TODO
*
@@ -64,7 +66,7 @@ public @interface ExternalDocs {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#externalDocumentationObject">ExternalDocumentation</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of documentation:
@@ -113,7 +115,7 @@ public @interface ExternalDocs {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/FormData.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/FormData.java
index 1c42387..657a15f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/FormData.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/FormData.java
@@ -16,9 +16,14 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import java.util.*;
+import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.exception.*;
/**
* Annotation that can be applied to a parameter of a {@link RestMethod @RestMethod} annotated method to identify it as a form post
@@ -68,24 +73,10 @@ import org.apache.juneau.rest.*;
public @interface FormData {
/**
- * Specify <jk>true</jk> if using multi-part parameters to represent collections and arrays.
- *
- * <p>
- * Normally, we expect single parameters to be specified in UON notation for representing collections of values
- * (e.g. <js>"key=(1,2,3)"</js>.
- * This annotation allows the use of multi-part parameters to represent collections (e.g.
- * <js>"key=1&key=2&key=3"</js>.
- *
- * <p>
- * This setting should only be applied to Java parameters of type array or Collection.
- */
- boolean multipart() default false;
-
- /**
* Specifies the {@link HttpPartParser} class used for parsing values from strings.
*
* <p>
- * The default value for this parser is inherited from the servlet/method which defaults to {@link UonPartParser}.
+ * The default value for this parser is inherited from the servlet/method which defaults to {@link OapiPartParser}.
* <br>You can use {@link SimplePartParser} to parse POJOs that are directly convertible from <code>Strings</code>.
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
@@ -147,18 +138,12 @@ public @interface FormData {
* <mk>required</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines whether this parameter is mandatory. The default value is <jk>false</jk>.
- *
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * Determines whether the parameter is mandatory.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*/
- String required() default "";
+ boolean required() default false;
//=================================================================================================================
// Attributes specific to parameters other than body
@@ -170,29 +155,41 @@ public @interface FormData {
* <p>
* The type of the parameter.
*
- * <p>
- * Note that per the Swagger specification, this value is required and must be a simple type (e.g. <js>"string"</js>, <js>"number"</js>...).
- * <br>However, Juneau allows for arbitrarily-complex POJOs such as beans in form parameters using UON notation, this attribute can be left off in lieu of a {@link #schema()}
- * attribute.
- *
* <p>
* The possible values are:
- * <ul>
- * <li><js>"string"</js>
- * <li><js>"number"</js>
- * <li><js>"integer"</js>
- * <li><js>"boolean"</js>
- * <li><js>"array"</js>
- * <li><js>"file"</js>
- * </ul>
- *
- * <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"string"</js>
+ * <br>Parameter must be a string or a POJO convertible from a string.
+ * <li>
+ * <js>"number"</js>
+ * <br>Parameter must be a number primitive or number object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Float</code> or <code>Double</code> depending on the size of the number.
+ * <li>
+ * <js>"integer"</js>
+ * <br>Parameter must be a integer/long primitive or integer/long object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Short</code>, <code>Integer</code>, or <code>Long</code> depending on the size of the number.
+ * <li>
+ * <js>"boolean"</js>
+ * <br>Parameter must be a boolean primitive or object.
+ * <li>
+ * <js>"array"</js>
+ * <br>Parameter must be an array or collection.
+ * <br>Elements must be strings or POJOs convertible from strings.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectList}.
+ * <li>
+ * <js>"object"</js>
+ * <br>Parameter must be a map or bean.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectMap}.
+ * <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
+ * <li>
+ * <js>"file"</js>
+ * <br>This type is currently not supported.
* </ul>
*
+ * <p>
+ * If the type is not specified, it will be auto-detected based on the parameter class type.
+ *
* <h5 class='section'>See Also:</h5>
* <ul class='doctree'>
* <li class='link'><a class='doclink' href='https://swagger.io/specification/#dataTypes'>Swagger specification > Data Types</a>
@@ -205,15 +202,48 @@ public @interface FormData {
*
* <p>
* The extending format for the previously mentioned <a href='https://swagger.io/specification/v2/#parameterType'>type</a>.
- * See <a href='https://swagger.io/specification/v2/#dataTypeFormat'>Data Type Formats</a> for further details.
*
- * <h5 class='section'>Notes:</h5>
+ * <p>
+ * The possible values are:
* <ul class='spaced-list'>
* <li>
- * The format is plain-text.
+ * <js>"int32"</js> - Signed 32 bits.
+ * <br>Only valid with type <js>"integer"</js>.
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"int64"</js> - Signed 64 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"float"</js> - 32-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"double"</js> - 64-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"byte"</js> - BASE-64 encoded characters.
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"password"</js> - Used to hint UIs the input needs to be obscured.
+ * <br>This format does not affect the serialization or parsing of the parameter.
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ * <br>Only valid with type <js>"object"</js>.
+ * <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/v2/#dataTypeFormat'>Swagger specification > Data Type Formats</a>
* </ul>
*/
String format() default "";
@@ -224,271 +254,279 @@ public @interface FormData {
* <p>
* Sets the ability to pass empty-valued parameters.
* <br>This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value.
- *
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Default value is <jk>false</jk>.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <br>The default value is <jk>false</jk>.
*/
- String allowEmptyValue() default "";
+ boolean allowEmptyValue() default false;
/**
- * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a regular expression according to the ECMA 262 regular expression dialect.
- * <li>
- * This string SHOULD be a valid regular expression.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Describes the type of items in the array.
+ * <p>
+ * Required if <code>type</code> is <js>"array"</js>.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*/
- String pattern() default "";
+ Items items() default @Items;
/**
* <mk>collectionFormat</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines the format of the array if type array is used.
- * <br>Possible values are:
- * <ul>
- * <li><js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
- * <li><js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
- * <li><js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
- * <li><js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
- * <li><js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
- * </ul>
+ * Determines the format of the array if <code>type</code> <js>"array"</js> is used.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*
- * <h5 class='section'>Notes:</h5>
+ * <br>Possible values are:
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
+ * <li>
+ * <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
+ * <li>
+ * <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ * <li>
+ * <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ * <li>
+ * <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
* </ul>
+ *
+ * <p>
+ * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
*/
String collectionFormat() default "";
/**
+ * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request.
+ * <br>(Note: "default" has no meaning for required parameters.)
+ *
+ * <p>
+ * Additionally, this value is used to create instances of POJOs that are then serialized as language-specific examples in the generated Swagger documentation
+ * if the examples are not defined in some other way.
+ *
+ * <p>
+ * The format of this value is a string.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Order placeOrder(
+ * <jk>@FormData</jk>(name=<js>"petId"</jk>, _default=<js>"100"</js>) <jk>long</jk> petId,
+ * <jk>@FormData</jk>(name=<js>"additionalInfo"</jk>, format=<js>"uon"</js>, _default=<js>"(rushOrder=false)"</js>) AdditionalInfo additionalInfo,
+ * <jk>@FormData</jk>(name=<js>"flags"</jk>, collectionFormat=<js>"uon"</js>, _default=<js>"@(new-customer)"</js>) String[] flags
+ * ) {...}
+ * </p>
+ */
+ String[] _default() default {};
+
+ /**
* <mk>maximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the maximum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String maximum() default "";
/**
+ * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines whether the maximum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>maximum</code>.
+ */
+ boolean exclusiveMaximum() default false;
+
+ /**
* <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the minimum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String minimum() default "";
/**
- * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines whether the minimum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>minimum</code>.
*/
- String multipleOf() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minLength() default "";
+ long minLength() default -1;
/**
- * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String maxItems() default "";
-
- /**
- * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * A string input is valid if it matches the specified regular expression pattern.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minItems() default "";
+ String pattern() default "";
/**
- * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * An array or collection is valid if its size is less than, or equal to, the value of this keyword.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String exclusiveMaximum() default "";
+ long maxItems() default -1;
/**
- * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String exclusiveMinimum() default "";
-
- /**
- * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * An array or collection is valid if its size is greater than, or equal to, the value of this keyword.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String uniqueItems() default "";
-
- /**
- * <mk>schema</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * The leading/trailing <code>{ }</code> characters are optional.
- * <br>The following two example are considered equivalent:
- * <ul>
- * <li><code>schema=<js>"{type:'string',format:'binary'}"</js></code>
- * <li><code>schema=<js>"type:'string',format:'binary'"</js></code>
- * <ul>
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- Schema schema() default @Schema;
-
+ long minItems() default -1;
+
/**
- * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Additionally, this method can be used to define a default value for a missing form data entry.
+ * If <jk>true</jk>, the input validates successfully if all of its elements are unique.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is any JSON.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ * <br>If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway).
+ * <br>Otherwise, the collection or array is checked for duplicate items.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String[] _default() default {};
+ boolean uniqueItems() default false;
/**
* <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON array or comma-delimited list.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If specified, the input validates successfully if it is equal to one of the elements in this array.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@FormData</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"AVAILABLE,PENDING,SOLD"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@FormData</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"['AVAILABLE','PENDING','SOLD']"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
*/
String[] _enum() default {};
/**
- * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
- Items items() default @Items;
+ String multipleOf() default "";
+
+ //=================================================================================================================
+ // Other
+ //=================================================================================================================
/**
- * TODO
+ * A serialized example of the parameter.
*
* <p>
* This attribute defines a JSON representation of the value that is used by {@link BasicRestInfoProvider} to construct
@@ -497,7 +535,7 @@ public @interface FormData {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -556,7 +594,9 @@ public @interface FormData {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * Automatic validation is NOT performed on input based on attributes in this value.
+ * <li>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Header.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Header.java
index a7f9ba0..dcae57e 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Header.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Header.java
@@ -16,9 +16,14 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import java.util.*;
+import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.exception.*;
/**
* Annotation that can be applied to a parameter of a {@link RestMethod @RestMethod} annotated method to identify it as a HTTP
@@ -58,7 +63,7 @@ public @interface Header {
* Specifies the {@link HttpPartParser} class used for parsing values from strings.
*
* <p>
- * The default value for this parser is inherited from the servlet/method which defaults to {@link UonPartParser}.
+ * The default value for this parser is inherited from the servlet/method which defaults to {@link OapiPartParser}.
* <br>You can use {@link SimplePartParser} to parse POJOs that are directly convertible from <code>Strings</code>.
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
@@ -112,18 +117,12 @@ public @interface Header {
* <mk>required</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines whether this parameter is mandatory. The default value is <jk>false</jk>.
- *
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * Determines whether the parameter is mandatory.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*/
- String required() default "";
+ boolean required() default false;
//=================================================================================================================
// Attributes specific to parameters other than body
@@ -135,27 +134,41 @@ public @interface Header {
* <p>
* The type of the parameter.
*
- * <p>
- * Note that per the Swagger specification, this value is required and must be a simple type (e.g. <js>"string"</js>, <js>"number"</js>...).
- * <br>However, Juneau allows for arbitrarily-complex POJOs such as beans in form parameters using UON notation, this attribute can be left off in lieu of a {@link #schema()}
- * attribute.
- *
* <p>
* The possible values are:
- * <ul>
- * <li><js>"string"</js>
- * <li><js>"number"</js>
- * <li><js>"integer"</js>
- * <li><js>"boolean"</js>
- * <li><js>"array"</js>
- * <li><js>"file"</js>
- * </ul>
- *
- * <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"string"</js>
+ * <br>Parameter must be a string or a POJO convertible from a string.
+ * <li>
+ * <js>"number"</js>
+ * <br>Parameter must be a number primitive or number object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Float</code> or <code>Double</code> depending on the size of the number.
+ * <li>
+ * <js>"integer"</js>
+ * <br>Parameter must be a integer/long primitive or integer/long object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Short</code>, <code>Integer</code>, or <code>Long</code> depending on the size of the number.
+ * <li>
+ * <js>"boolean"</js>
+ * <br>Parameter must be a boolean primitive or object.
+ * <li>
+ * <js>"array"</js>
+ * <br>Parameter must be an array or collection.
+ * <br>Elements must be strings or POJOs convertible from strings.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectList}.
+ * <li>
+ * <js>"object"</js>
+ * <br>Parameter must be a map or bean.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectMap}.
+ * <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
+ * <li>
+ * <js>"file"</js>
+ * <br>This type is currently not supported.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/#dataTypes'>Swagger specification > Data Types</a>
* </ul>
*/
String type() default "";
@@ -165,269 +178,322 @@ public @interface Header {
*
* <p>
* The extending format for the previously mentioned <a href='https://swagger.io/specification/v2/#parameterType'>type</a>.
- * See <a href='https://swagger.io/specification/v2/#dataTypeFormat'>Data Type Formats</a> for further details.
*
- * <h5 class='section'>Notes:</h5>
+ * <p>
+ * The possible values are:
* <ul class='spaced-list'>
* <li>
- * The format is plain-text.
+ * <js>"int32"</js> - Signed 32 bits.
+ * <br>Only valid with type <js>"integer"</js>.
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"int64"</js> - Signed 64 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"float"</js> - 32-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"double"</js> - 64-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"byte"</js> - BASE-64 encoded characters.
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"password"</js> - Used to hint UIs the input needs to be obscured.
+ * <br>This format does not affect the serialization or parsing of the parameter.
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ * <br>Only valid with type <js>"object"</js>.
+ * <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/v2/#dataTypeFormat'>Swagger specification > Data Type Formats</a>
* </ul>
*/
String format() default "";
/**
- * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a regular expression according to the ECMA 262 regular expression dialect.
- * <li>
- * This string SHOULD be a valid regular expression.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Describes the type of items in the array.
+ * <p>
+ * Required if <code>type</code> is <js>"array"</js>.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*/
- String pattern() default "";
-
+ Items items() default @Items;
+
/**
* <mk>collectionFormat</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines the format of the array if type array is used.
- * <br>Possible values are:
- * <ul>
- * <li><js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
- * <li><js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
- * <li><js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
- * <li><js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
- * </ul>
+ * Determines the format of the array if <code>type</code> <js>"array"</js> is used.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*
- * <h5 class='section'>Notes:</h5>
+ * <br>Possible values are:
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
+ * <li>
+ * <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
+ * <li>
+ * <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ * <li>
+ * <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ * <li>
+ * <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
+ * <li>
* </ul>
+ *
+ * <p>
+ * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
*/
String collectionFormat() default "";
/**
+ * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request.
+ * <br>(Note: "default" has no meaning for required parameters.)
+ *
+ * <p>
+ * Additionally, this value is used to create instances of POJOs that are then serialized as language-specific examples in the generated Swagger documentation
+ * if the examples are not defined in some other way.
+ *
+ * <p>
+ * The format of this value is a string.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Order placeOrder(
+ * <jk>@Header</jk>(name=<js>"X-PetId"</jk>, _default=<js>"100"</js>) <jk>long</jk> petId,
+ * <jk>@Header</jk>(name=<js>"X-AdditionalInfo"</jk>, format=<js>"uon"</js>, _default=<js>"(rushOrder=false)"</js>) AdditionalInfo additionalInfo,
+ * <jk>@Header</jk>(name=<js>"X-Flags"</jk>, collectionFormat=<js>"uon"</js>, _default=<js>"@(new-customer)"</js>) String[] flags
+ * ) {...}
+ * </p>
+ */
+ String[] _default() default {};
+
+ /**
* <mk>maximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the maximum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String maximum() default "";
/**
+ * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines whether the maximum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>maximum</code>.
+ */
+ boolean exclusiveMaximum() default false;
+
+ /**
* <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the minimum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String minimum() default "";
-
+
/**
- * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines whether the minimum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>minimum</code>.
*/
- String multipleOf() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minLength() default "";
+ long minLength() default -1;
/**
- * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String maxItems() default "";
-
- /**
- * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * A string input is valid if it matches the specified regular expression pattern.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minItems() default "";
+ String pattern() default "";
/**
- * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * An array or collection is valid if its size is less than, or equal to, the value of this keyword.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String exclusiveMaximum() default "";
+ long maxItems() default -1;
/**
- * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String exclusiveMinimum() default "";
-
- /**
- * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * An array or collection is valid if its size is greater than, or equal to, the value of this keyword.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String uniqueItems() default "";
-
- /**
- * <mk>schema</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * The leading/trailing <code>{ }</code> characters are optional.
- * <br>The following two example are considered equivalent:
- * <ul>
- * <li><code>schema=<js>"{type:'string',format:'binary'}"</js></code>
- * <li><code>schema=<js>"type:'string',format:'binary'"</js></code>
- * <ul>
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- Schema schema() default @Schema;
-
+ long minItems() default -1;
+
/**
- * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Additionally, this method can be used to define a default value for a missing header entry.
+ * If <jk>true</jk> the input validates successfully if all of its elements are unique.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is any JSON.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ * <br>If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway).
+ * <br>Otherwise, the collection or array is checked for duplicate items.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String[] _default() default {};
+ boolean uniqueItems() default false;
/**
* <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON array or comma-delimited list.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If specified, the input validates successfully if it is equal to one of the elements in this array.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Header</ja>(
+ * name=<js>"X-Status"</js>,
+ * _enum=<js>"AVAILABLE,PENDING,SOLD"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Header</ja>(
+ * name=<js>"X-Status"</js>,
+ * _enum=<js>"['AVAILABLE','PENDING','SOLD']"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
*/
String[] _enum() default {};
/**
- * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
- Items items() default @Items;
+ String multipleOf() default "";
+
+ //=================================================================================================================
+ // Other
+ //=================================================================================================================
/**
- * TODO
+ * A serialized example of the parameter.
*
* <p>
* This attribute defines a JSON representation of the value that is used by {@link BasicRestInfoProvider} to construct
@@ -436,7 +502,7 @@ public @interface Header {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -497,7 +563,9 @@ public @interface Header {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
+ * <li>
+ * Automatic validation is NOT performed on input based on attributes in this value.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java
index 3c881d7..1e46e18 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
+
/**
* Swagger schema annotation.
*
@@ -147,7 +149,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -161,7 +163,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minLength() default "";
+ long minLength() default -1;
/**
* <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -175,7 +177,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxItems() default "";
+ long maxItems() default -1;
/**
* <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -189,7 +191,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minItems() default "";
+ long minItems() default -1;
/**
* <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -203,7 +205,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMaximum() default "";
+ boolean exclusiveMaximum() default false;
/**
* <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -217,7 +219,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMinimum() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -231,7 +233,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String uniqueItems() default "";
+ boolean uniqueItems() default false;
/**
* <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -276,10 +278,15 @@ public @interface Items {
String $ref() default "";
/**
+ * TODO
+ */
+ SubItems items() default @SubItems;
+
+ /**
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of an Items object:
@@ -346,7 +353,7 @@ public @interface Items {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
@@ -366,4 +373,5 @@ public @interface Items {
* </ul>
*/
String[] value() default {};
+
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/License.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/License.java
index f4234df..0357834 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/License.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/License.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
+
/**
* Swagger schema annotation.
*
@@ -116,7 +118,7 @@ public @interface License {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
index 361587a..0495784 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.annotation;
+import org.apache.juneau.json.*;
+
/**
* Extended annotation for {@link RestMethod#swagger() RestMethod.swagger()}.
*
@@ -84,7 +86,7 @@ public @interface MethodSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is either a comma-delimited list of simple strings or a JSON array.
+ * The format is either a comma-delimited list of simple strings or a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
* (e.g. <js>"$L{my.localized.variable}"</js>).
@@ -130,7 +132,7 @@ public @interface MethodSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is either a comma-delimited list of simple strings or a JSON array.
+ * The format is either a comma-delimited list of simple strings or a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
* (e.g. <js>"$L{my.localized.variable}"</js>).
@@ -149,7 +151,7 @@ public @interface MethodSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is either a comma-delimited list of simple strings or a JSON array.
+ * The format is either a comma-delimited list of simple strings or a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
* (e.g. <js>"$L{my.localized.variable}"</js>).
@@ -299,7 +301,7 @@ public @interface MethodSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <br>Comments and whitespace are ignored.
* <br>The leading and trailing <js>'{'</js>/<js>'}'</js> characters are optional.
@@ -317,7 +319,7 @@ public @interface MethodSwagger {
* Free-form value for the swagger of a resource method.
*
* <p>
- * This is a JSON object that makes up the swagger information for this resource method.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this resource method.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of a resource method:
@@ -376,7 +378,7 @@ public @interface MethodSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
index 812c0a9..9093e06 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
@@ -17,8 +17,12 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.exception.*;
/**
* Annotation that can be applied to a parameter of a {@link RestMethod @RestMethod} annotated method to identify it as a variable
@@ -49,7 +53,7 @@ public @interface Path {
* Specifies the {@link HttpPartParser} class used for parsing values from strings.
*
* <p>
- * The default value for this parser is inherited from the servlet/method which defaults to {@link UonPartParser}.
+ * The default value for this parser is inherited from the servlet/method which defaults to {@link OapiPartParser}.
* <br>You can use {@link SimplePartParser} to parse POJOs that are directly convertible from <code>Strings</code>.
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
@@ -127,28 +131,41 @@ public @interface Path {
* <p>
* The type of the parameter.
*
- * <p>
- * Note that per the Swagger specification, this value is required and must be a simple type (e.g. <js>"string"</js>, <js>"number"</js>...).
- * <br>However, Juneau allows for arbitrarily-complex POJOs such as beans in form parameters using UON notation, this attribute can be left off in lieu of a {@link #schema()}
- * attribute.
- *
* <p>
* The possible values are:
- * <ul>
- * <li><js>"string"</js>
- * <li><js>"number"</js>
- * <li><js>"integer"</js>
- * <li><js>"boolean"</js>
- * <li><js>"array"</js>
- * <li><js>"file"</js>
- * </ul>
- *
- *
- * <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"string"</js>
+ * <br>Parameter must be a string or a POJO convertible from a string.
+ * <li>
+ * <js>"number"</js>
+ * <br>Parameter must be a number primitive or number object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Float</code> or <code>Double</code> depending on the size of the number.
+ * <li>
+ * <js>"integer"</js>
+ * <br>Parameter must be a integer/long primitive or integer/long object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Short</code>, <code>Integer</code>, or <code>Long</code> depending on the size of the number.
+ * <li>
+ * <js>"boolean"</js>
+ * <br>Parameter must be a boolean primitive or object.
+ * <li>
+ * <js>"array"</js>
+ * <br>Parameter must be an array or collection.
+ * <br>Elements must be strings or POJOs convertible from strings.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectList}.
+ * <li>
+ * <js>"object"</js>
+ * <br>Parameter must be a map or bean.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectMap}.
+ * <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
+ * <li>
+ * <js>"file"</js>
+ * <br>This type is currently not supported.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/#dataTypes'>Swagger specification > Data Types</a>
* </ul>
*/
String type() default "";
@@ -158,194 +175,254 @@ public @interface Path {
*
* <p>
* The extending format for the previously mentioned <a href='https://swagger.io/specification/v2/#parameterType'>type</a>.
- * See <a href='https://swagger.io/specification/v2/#dataTypeFormat'>Data Type Formats</a> for further details.
*
- * <h5 class='section'>Notes:</h5>
+ * <p>
+ * The possible values are:
* <ul class='spaced-list'>
* <li>
- * The format is plain-text.
+ * <js>"int32"</js> - Signed 32 bits.
+ * <br>Only valid with type <js>"integer"</js>.
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"int64"</js> - Signed 64 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"float"</js> - 32-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"double"</js> - 64-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"byte"</js> - BASE-64 encoded characters.
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"password"</js> - Used to hint UIs the input needs to be obscured.
+ * <br>This format does not affect the serialization or parsing of the parameter.
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ * <br>Only valid with type <js>"object"</js>.
+ * <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/v2/#dataTypeFormat'>Swagger specification > Data Type Formats</a>
* </ul>
*/
String format() default "";
/**
- * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a regular expression according to the ECMA 262 regular expression dialect.
- * <li>
- * This string SHOULD be a valid regular expression.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Describes the type of items in the array.
+ * <p>
+ * Required if <code>type</code> is <js>"array"</js>.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*/
- String pattern() default "";
-
+ Items items() default @Items;
+
/**
* <mk>collectionFormat</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines the format of the array if type array is used.
- * <br>Possible values are:
- * <ul>
- * <li><js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
- * <li><js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
- * <li><js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
- * <li><js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
- * </ul>
+ * Determines the format of the array if <code>type</code> <js>"array"</js> is used.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*
- * <h5 class='section'>Notes:</h5>
+ * <br>Possible values are:
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
+ * <li>
+ * <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
+ * <li>
+ * <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ * <li>
+ * <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ * <li>
+ * <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
+ * <li>
* </ul>
+ *
+ * <p>
+ * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
*/
String collectionFormat() default "";
/**
* <mk>maximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the maximum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String maximum() default "";
/**
- * <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines whether the maximum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>maximum</code>.
*/
- String minimum() default "";
+ boolean exclusiveMaximum() default false;
/**
- * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the minimum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
- String multipleOf() default "";
+ String minimum() default "";
/**
- * <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines whether the minimum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, Must be accompanied with <code>minimum</code>.
*/
- String maxLength() default "";
-
+ boolean exclusiveMinimum() default false;
+
/**
- * <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minLength() default "";
+ long maxLength() default -1;
/**
- * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String exclusiveMaximum() default "";
-
- /**
- * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String exclusiveMinimum() default "";
+ long minLength() default -1;
/**
- * <mk>schema</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * The leading/trailing <code>{ }</code> characters are optional.
- * <br>The following two example are considered equivalent:
- * <ul>
- * <li><code>schema=<js>"{type:'string',format:'binary'}"</js></code>
- * <li><code>schema=<js>"type:'string',format:'binary'"</js></code>
- * <ul>
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string input is valid if it matches the specified regular expression pattern.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- Schema schema() default @Schema;
+ String pattern() default "";
/**
* <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON array or comma-delimited list.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If specified, the input validates successfully if it is equal to one of the elements in this array.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/pet/findByStatus/{status}"</js>)
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Path</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"AVAILABLE,PENDING,SOLD"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
+ * <p class='bcode w800'>
+ * <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/pet/findByStatus/{status}"</js>)
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Path</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"['AVAILABLE','PENDING','SOLD']"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
*/
String[] _enum() default {};
+
+ /**
+ * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ */
+ String multipleOf() default "";
+ //=================================================================================================================
+ // Other
+ //=================================================================================================================
+
/**
- * TODO
+ * A serialized example of the parameter.
*
* <p>
* This attribute defines a JSON representation of the value that is used by {@link BasicRestInfoProvider} to construct
@@ -354,7 +431,7 @@ public @interface Path {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -367,7 +444,7 @@ public @interface Path {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of the Path object:
@@ -417,7 +494,9 @@ public @interface Path {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
+ * <li>
+ * Automatic validation is NOT performed on input based on attributes in this value.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Query.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Query.java
index f2af59e..9579472 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Query.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Query.java
@@ -16,9 +16,14 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import java.util.*;
+import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.exception.*;
/**
* Identical to {@link FormData @FormData}, but only retrieves the parameter from the URL string, not URL-encoded form
@@ -64,24 +69,10 @@ import org.apache.juneau.rest.*;
public @interface Query {
/**
- * Specify <jk>true</jk> if using multi-part parameters to represent collections and arrays.
- *
- * <p>
- * Normally, we expect single parameters to be specified in UON notation for representing collections of values
- * (e.g. <js>"&key=(1,2,3)"</js>.
- * This annotation allows the use of multi-part parameters to represent collections
- * (e.g. <js>"&key=1&key=2&key=3"</js>.
- *
- * <p>
- * This setting should only be applied to Java parameters of type array or Collection.
- */
- boolean multipart() default false;
-
- /**
* Specifies the {@link HttpPartParser} class used for parsing values from strings.
*
* <p>
- * The default value for this parser is inherited from the servlet/method which defaults to {@link UonPartParser}.
+ * The default value for this parser is inherited from the servlet/method which defaults to {@link OapiPartParser}.
* <br>You can use {@link SimplePartParser} to parse POJOs that are directly convertible from <code>Strings</code>.
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
@@ -143,18 +134,12 @@ public @interface Query {
* <mk>required</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines whether this parameter is mandatory. The default value is <jk>false</jk>.
- *
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * Determines whether the parameter is mandatory.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*/
- String required() default "";
+ boolean required() default false;
//=================================================================================================================
// Attributes specific to parameters other than body
@@ -166,27 +151,36 @@ public @interface Query {
* <p>
* The type of the parameter.
*
- * <p>
- * Note that per the Swagger specification, this value is required and must be a simple type (e.g. <js>"string"</js>, <js>"number"</js>...).
- * <br>However, Juneau allows for arbitrarily-complex POJOs such as beans in form parameters using UON notation, this attribute can be left off in lieu of a {@link #schema()}
- * attribute.
- *
* <p>
* The possible values are:
- * <ul>
- * <li><js>"string"</js>
- * <li><js>"number"</js>
- * <li><js>"integer"</js>
- * <li><js>"boolean"</js>
- * <li><js>"array"</js>
- * <li><js>"file"</js>
- * </ul>
- *
- * <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"string"</js>
+ * <br>Parameter must be a string or a POJO convertible from a string.
+ * <li>
+ * <js>"number"</js>
+ * <br>Parameter must be a number primitive or number object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Float</code> or <code>Double</code> depending on the size of the number.
+ * <li>
+ * <js>"integer"</js>
+ * <br>Parameter must be a integer/long primitive or integer/long object.
+ * <br>If parameter is <code>Object</code>, creates either a <code>Short</code>, <code>Integer</code>, or <code>Long</code> depending on the size of the number.
+ * <li>
+ * <js>"boolean"</js>
+ * <br>Parameter must be a boolean primitive or object.
+ * <li>
+ * <js>"array"</js>
+ * <br>Parameter must be an array or collection.
+ * <br>Elements must be strings or POJOs convertible from strings.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectList}.
+ * <li>
+ * <js>"object"</js>
+ * <br>Parameter must be a map or bean.
+ * <br>If parameter is <code>Object</code>, creates an {@link ObjectMap}.
+ * <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
+ * <li>
+ * <js>"file"</js>
+ * <br>This type is currently not supported.
* </ul>
*
* <h5 class='section'>See Also:</h5>
@@ -201,15 +195,48 @@ public @interface Query {
*
* <p>
* The extending format for the previously mentioned <a href='https://swagger.io/specification/v2/#parameterType'>type</a>.
- * See <a href='https://swagger.io/specification/v2/#dataTypeFormat'>Data Type Formats</a> for further details.
*
- * <h5 class='section'>Notes:</h5>
+ * <p>
+ * The possible values are:
* <ul class='spaced-list'>
* <li>
- * The format is plain-text.
+ * <js>"int32"</js> - Signed 32 bits.
+ * <br>Only valid with type <js>"integer"</js>.
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"int64"</js> - Signed 64 bits.
+ * <br>Only valid with type <js>"integer"</js>.
+ * <li>
+ * <js>"float"</js> - 32-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"double"</js> - 64-bit floating point number.
+ * <br>Only valid with type <js>"number"</js>.
+ * <li>
+ * <js>"byte"</js> - BASE-64 encoded characters.
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
+ * <br>Only valid with type <js>"string"</js>.
+ * <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
+ * <li>
+ * <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
+ * <br>Only valid with type <js>"string"</js>.
+ * <li>
+ * <js>"password"</js> - Used to hint UIs the input needs to be obscured.
+ * <br>This format does not affect the serialization or parsing of the parameter.
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
+ * <br>Only valid with type <js>"object"</js>.
+ * <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul class='doctree'>
+ * <li class='link'><a class='doclink' href='https://swagger.io/specification/v2/#dataTypeFormat'>Swagger specification > Data Type Formats</a>
* </ul>
*/
String format() default "";
@@ -220,270 +247,280 @@ public @interface Query {
* <p>
* Sets the ability to pass empty-valued parameters.
* <br>This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value.
- *
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Default value is <jk>false</jk>.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * The default value is <jk>false</jk>.
*/
- String allowEmptyValue() default "";
+ boolean allowEmptyValue() default false;
/**
- * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a regular expression according to the ECMA 262 regular expression dialect.
- * <li>
- * This string SHOULD be a valid regular expression.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Describes the type of items in the array.
+ * <p>
+ * Required if <code>type</code> is <js>"array"</js>.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*/
- String pattern() default "";
+ Items items() default @Items;
/**
* <mk>collectionFormat</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Determines the format of the array if type array is used.
- * <br>Possible values are:
- * <ul>
- * <li><js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
- * <li><js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
- * <li><js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
- * <li><js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
- * <li><js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
- * </ul>
+ * Determines the format of the array if <code>type</code> <js>"array"</js> is used.
+ * <br>Can only be used if <code>type</code> is <js>"array"</js>.
*
- * <h5 class='section'>Notes:</h5>
+ * <br>Possible values are:
* <ul class='spaced-list'>
* <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
+ * <li>
+ * <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
+ * <li>
+ * <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
+ * <li>
+ * <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
+ * <li>
+ * <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>).
+ * <li>
+ * <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
* </ul>
+ *
+ * <p>
+ * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
*/
String collectionFormat() default "";
/**
+ * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request.
+ * <br>(Note: "default" has no meaning for required parameters.)
+ *
+ * <p>
+ * Additionally, this value is used to create instances of POJOs that are then serialized as language-specific examples in the generated Swagger documentation
+ * if the examples are not defined in some other way.
+ *
+ * <p>
+ * The format of this value is a string.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Order placeOrder(
+ * <jk>@Query</jk>(name=<js>"petId"</jk>, _default=<js>"100"</js>) <jk>long</jk> petId,
+ * <jk>@Query</jk>(name=<js>"additionalInfo"</jk>, format=<js>"uon"</js>, _default=<js>"(rushOrder=false)"</js>) AdditionalInfo additionalInfo,
+ * <jk>@Query</jk>(name=<js>"flags"</jk>, collectionFormat=<js>"uon"</js>, _default=<js>"@(new-customer)"</js>) String[] flags
+ * ) {...}
+ * </p>
+ */
+ String[] _default() default {};
+
+ /**
* <mk>maximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the maximum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String maximum() default "";
/**
+ * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ *
+ * <p>
+ * Defines whether the maximum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, must be accompanied with <code>maximum</code>.
+ */
+ boolean exclusiveMaximum() default false;
+
+ /**
* <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON number.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines the minimum value for a parameter of numeric types.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
String minimum() default "";
-
+
/**
- * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Defines whether the minimum is matched exclusively.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
+ * <br>If <jk>true</jk>, Must be accompanied with <code>minimum</code>.
*/
- String multipleOf() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
+ * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
+ * <br>The value <code>-1</code> is always ignored.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minLength() default "";
+ long minLength() default -1;
/**
- * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String maxItems() default "";
-
- /**
- * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * A string input is valid if it matches the specified regular expression pattern.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"string"</js>.
*/
- String minItems() default "";
+ String pattern() default "";
/**
- * <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * An array or collection is valid if its size is less than, or equal to, the value of this keyword.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String exclusiveMaximum() default "";
+ long maxItems() default -1;
/**
- * <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is numeric.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String exclusiveMinimum() default "";
-
- /**
- * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * An array or collection is valid if its size is greater than, or equal to, the value of this keyword.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is boolean.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
- */
- String uniqueItems() default "";
-
- /**
- * <mk>schema</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * The leading/trailing <code>{ }</code> characters are optional.
- * <br>The following two example are considered equivalent:
- * <ul>
- * <li><code>schema=<js>"{type:'string',format:'binary'}"</js></code>
- * <li><code>schema=<js>"type:'string',format:'binary'"</js></code>
- * <ul>
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- Schema schema() default @Schema;
-
+ long minItems() default -1;
+
/**
- * <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * Additionally, this method can be used to define a default value for a missing query entry.
+ * If <jk>true</jk> the input validates successfully if all of its elements are unique.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is any JSON.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ * <br>If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway).
+ * <br>Otherwise, the collection or array is checked for duplicate items.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"array"</js>.
*/
- String[] _default() default {};
-
+ boolean uniqueItems() default false;
+
/**
* <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON array or comma-delimited list.
- * <br>Multiple lines are concatenated with newlines.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * If specified, the input validates successfully if it is equal to one of the elements in this array.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
+ * <br>Multiple lines are concatenated with newlines.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Query</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"AVAILABLE,PENDING,SOLD"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
+ * <p class='bcode w800'>
+ * <jk>public</jk> Collection<Pet> findPetsByStatus(
+ * <ja>@Query</ja>(
+ * name=<js>"status"</js>,
+ * _enum=<js>"['AVAILABLE','PENDING','SOLD']"</js>,
+ * ) PetStatus status
+ * ) {...}
+ * </p>
*/
String[] _enum() default {};
/**
- * <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
+ * <mk>multipleOf</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
- * <h5 class='section'>Notes:</h5>
- * <ul class='spaced-list'>
- * <li>
- * The format is a JSON object.
- * <li>
- * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
- * (e.g. <js>"$L{my.localized.variable}"</js>).
- * </ul>
+ * <p>
+ * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
+ * <br>The value must be a valid JSON number.
+ *
+ * <p>
+ * If validation is not met, the method call will throw a {@link BadRequest}.
+ *
+ * <p>
+ * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
*/
- Items items() default @Items;
+ String multipleOf() default "";
+
+ //=================================================================================================================
+ // Other
+ //=================================================================================================================
/**
- * TODO
+ * A serialized example of the parameter.
*
* <p>
* This attribute defines a JSON representation of the value that is used by {@link BasicRestInfoProvider} to construct
@@ -492,7 +529,7 @@ public @interface Query {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -505,7 +542,7 @@ public @interface Query {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#parameterObject">Parameter</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of the Query object:
@@ -565,7 +602,9 @@ public @interface Query {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * Automatic validation is NOT performed on input based on attributes in this value.
+ * <li>
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
index 1df8975..327cad6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java
@@ -12,6 +12,8 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.annotation;
+import org.apache.juneau.json.*;
+
/**
* Extended annotation for {@link RestResource#swagger() @RestResource.swagger()}.
*
@@ -96,7 +98,7 @@ public @interface ResourceSwagger {
* Defines the swagger field <code>/info/contact</code>.
*
* <p>
- * A simplified JSON string with the following fields:
+ * A {@link JsonSerializer#DEFAULT_LAX Simple-JSON} string with the following fields:
* <p class='bcode'>
* {
* name: string,
@@ -139,7 +141,7 @@ public @interface ResourceSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -177,7 +179,7 @@ public @interface ResourceSwagger {
* It is used to populate the Swagger license field and to display on HTML pages.
*
* <p>
- * A simplified JSON string with the following fields:
+ * A {@link JsonSerializer#DEFAULT_LAX Simple-JSON} string with the following fields:
* <p class='bcode'>
* {
* name: string,
@@ -202,7 +204,7 @@ public @interface ResourceSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -221,7 +223,7 @@ public @interface ResourceSwagger {
* It is used to populate the Swagger tags field and to display on HTML pages.
*
* <p>
- * A simplified JSON string with the following fields:
+ * A {@link JsonSerializer#DEFAULT_LAX Simple-JSON} string with the following fields:
* <p class='bcode'>
* [
* {
@@ -252,7 +254,7 @@ public @interface ResourceSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON array.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -315,7 +317,7 @@ public @interface ResourceSwagger {
* Free-form value for the swagger of a resource.
*
* <p>
- * This is a JSON object that makes up the swagger information for this resource.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this resource.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of a resource:
@@ -405,7 +407,7 @@ public @interface ResourceSwagger {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Response.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Response.java
index 2e357c3..aae3f7b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Response.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Response.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.exception.*;
import org.apache.juneau.rest.helper.*;
@@ -157,7 +158,7 @@ public @interface Response {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
@@ -184,7 +185,7 @@ public @interface Response {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is any JSON.
+ * The format is any {@link JsonSerializer#DEFAULT_LAX Simple-JSON}.
* <br>Multiple lines are concatenated with newlines.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
@@ -204,12 +205,12 @@ public @interface Response {
* TODO
*
* <p>
- * The format is a JSON object with keys as media types and values as string representations of the body response.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object with keys as media types and values as string representations of the body response.
*
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
@@ -224,7 +225,7 @@ public @interface Response {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#responseObject">Response</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of the Response object:
@@ -281,7 +282,7 @@ public @interface Response {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"code"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseHeader.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseHeader.java
index 04d9c54..d8e403b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseHeader.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseHeader.java
@@ -18,6 +18,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.oapi.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.utils.*;
@@ -129,7 +131,7 @@ public @interface ResponseHeader {
* Specifies the {@link HttpPartSerializer} class used for serializing values.
*
* <p>
- * The default value for this parser is inherited from the servlet/method which defaults to {@link SimpleUonPartSerializer}.
+ * The default value for this parser is inherited from the servlet/method which defaults to {@link OapiPartSerializer}.
*/
Class<? extends HttpPartSerializer> serializer() default HttpPartSerializer.Null.class;
@@ -289,7 +291,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -303,7 +305,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minLength() default "";
+ long minLength() default -1;
/**
* <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -317,7 +319,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxItems() default "";
+ long maxItems() default -1;
/**
* <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -331,7 +333,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minItems() default "";
+ long minItems() default -1;
/**
* <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -345,7 +347,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMaximum() default "";
+ boolean exclusiveMaximum() default false;
/**
* <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -359,7 +361,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMinimum() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -373,7 +375,7 @@ public @interface ResponseHeader {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String uniqueItems() default "";
+ boolean uniqueItems() default false;
/**
* <mk>items</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#headerObject">Header</a> object.
@@ -381,7 +383,7 @@ public @interface ResponseHeader {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -396,7 +398,7 @@ public @interface ResponseHeader {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is any JSON.
+ * The format is any {@link JsonSerializer#DEFAULT_LAX Simple-JSON}.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -411,7 +413,7 @@ public @interface ResponseHeader {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON array or comma-delimited list.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -430,7 +432,7 @@ public @interface ResponseHeader {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -491,7 +493,7 @@ public @interface ResponseHeader {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseStatus.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseStatus.java
index 66021b2..5dec704 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseStatus.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/ResponseStatus.java
@@ -16,6 +16,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.utils.*;
/**
@@ -188,7 +189,7 @@ public @interface ResponseStatus {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"code"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index 85d524b..c3ac989 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
import org.apache.juneau.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.remoteable.*;
import org.apache.juneau.rest.*;
@@ -758,7 +759,7 @@ public @interface RestMethod {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is JSON.
+ * The format is {@link JsonSerializer#DEFAULT_LAX Simple-JSON}.
* <br>Multiple lines are concatenated with newlines.
* <li>
* The starting and ending <js>'{'</js>/<js>'}'</js> characters around the entire value are optional.
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index 4e694b0..6364285 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -21,6 +21,7 @@ import org.apache.juneau.*;
import org.apache.juneau.config.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.uon.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.serializer.*;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Schema.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Schema.java
index 6c71ebd..f14e2c6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Schema.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Schema.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
import org.apache.juneau.rest.*;
/**
@@ -141,7 +142,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is any JSON.
+ * The format is any {@link JsonSerializer#DEFAULT_LAX Simple-JSON}.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -190,7 +191,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMaximum() default "";
+ boolean exclusiveMaximum() default false;
/**
* <mk>minimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -218,7 +219,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMinimum() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>maxLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -232,7 +233,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -246,7 +247,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minLength() default "";
+ long minLength() default -1;
/**
* <mk>pattern</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -282,7 +283,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxItems() default "";
+ long maxItems() default -1;
/**
* <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -296,7 +297,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minItems() default "";
+ long minItems() default -1;
/**
* <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -310,7 +311,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String uniqueItems() default "";
+ boolean uniqueItems() default false;
/**
@@ -319,7 +320,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -335,7 +336,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -377,7 +378,7 @@ public @interface Schema {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String required() default "";
+ boolean required() default false;
/**
* <mk>enum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -385,7 +386,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON array or comma-delimited list.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} array or comma-delimited list.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -448,7 +449,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -463,7 +464,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -478,7 +479,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -493,7 +494,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -508,7 +509,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -523,14 +524,14 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String readOnly() default "";
+ boolean readOnly() default false;
/**
* <mk>xml</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#schemaObject">Schema</a> object.
@@ -538,7 +539,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -553,7 +554,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -572,7 +573,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object or plain text string.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object or plain text string.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -590,7 +591,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -599,6 +600,9 @@ public @interface Schema {
*/
String[] examples() default {};
+ /**
+ * TODO
+ */
boolean ignore() default false;
/**
@@ -646,7 +650,7 @@ public @interface Schema {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/SubItems.java
similarity index 93%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java
copy to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/SubItems.java
index 3c881d7..1fed64c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Items.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/SubItems.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
+
/**
* Swagger schema annotation.
*
@@ -35,7 +37,7 @@ import java.lang.annotation.*;
@Target({PARAMETER,TYPE})
@Retention(RUNTIME)
@Inherited
-public @interface Items {
+public @interface SubItems {
/**
* <mk>type</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -147,7 +149,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxLength() default "";
+ long maxLength() default -1;
/**
* <mk>minLength</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -161,7 +163,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minLength() default "";
+ long minLength() default -1;
/**
* <mk>maxItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -175,7 +177,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String maxItems() default "";
+ long maxItems() default -1;
/**
* <mk>minItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -189,7 +191,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String minItems() default "";
+ long minItems() default -1;
/**
* <mk>exclusiveMaximum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -203,7 +205,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMaximum() default "";
+ boolean exclusiveMaximum() default false;
/**
* <mk>exclusiveMinimum</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -217,7 +219,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String exclusiveMinimum() default "";
+ boolean exclusiveMinimum() default false;
/**
* <mk>uniqueItems</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -231,7 +233,7 @@ public @interface Items {
* (e.g. <js>"$L{my.localized.variable}"</js>).
* </ul>
*/
- String uniqueItems() default "";
+ boolean uniqueItems() default false;
/**
* <mk>default</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
@@ -279,7 +281,7 @@ public @interface Items {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#itemsObject">Items</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this field.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this field.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of an Items object:
@@ -346,7 +348,7 @@ public @interface Items {
* <li>
* Note that the only swagger field you can't specify using this value is <js>"name"</js> whose value needs to be known during servlet initialization.
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
@@ -366,4 +368,9 @@ public @interface Items {
* </ul>
*/
String[] value() default {};
+
+ /**
+ * TODO
+ */
+ String[] items() default {};
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Tag.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Tag.java
index 356e5bf..2f4aa58 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Tag.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Tag.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.json.*;
+
/**
* Swagger schema annotation.
*
@@ -83,7 +85,7 @@ public @interface Tag {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -98,7 +100,7 @@ public @interface Tag {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <br>Multiple lines are concatenated with newlines.
* <li>
* Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a>
@@ -111,7 +113,7 @@ public @interface Tag {
* Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#tagObject">Tag</a> object.
*
* <p>
- * This is a JSON object that makes up the swagger information for this Tag object.
+ * This is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object that makes up the swagger information for this Tag object.
*
* <p>
* The following are completely equivalent ways of defining the swagger description of the resource tags:
@@ -174,7 +176,7 @@ public @interface Tag {
* <h5 class='section'>Notes:</h5>
* <ul class='spaced-list'>
* <li>
- * The format is a Simplified JSON object.
+ * The format is a {@link JsonSerializer#DEFAULT_LAX Simple-JSON} object.
* <li>
* The leading/trailing <code>{ }</code> characters are optional.
* <br>The following two example are considered equivalent:
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
index 20de6fd..9165424 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
@@ -54,9 +54,9 @@ public class MockRest implements MockHttpConnection {
private final RestContext rc;
- private MockRest(Class<?> c) throws Exception {
+ private MockRest(Class<?> c, boolean debug) throws Exception {
if (! CONTEXTS.containsKey(c))
- CONTEXTS.put(c, RestContext.create(c.newInstance()).build().postInit().postInitChildFirst());
+ CONTEXTS.put(c, RestContext.create(c.newInstance()).logger(debug ? BasicRestLogger.class : NoOpRestLogger.class).build().postInit().postInitChildFirst());
rc = CONTEXTS.get(c);
}
@@ -69,13 +69,28 @@ public class MockRest implements MockHttpConnection {
* For testing conveniences, this method wraps all exceptions in a RuntimeException so that you can easily define mocks as reusable fields.
*/
public static MockRest create(Class<?> c) throws RuntimeException {
+ return create(c, false);
+ }
+
+ /**
+ * Create a new mock REST interface
+ *
+ * @param c The REST class.
+ * @param debug
+ * If <jk>true</jk>, the REST interface will use the {@link BasicRestLogger} for logging.
+ * <br>Otherwise, uses {@link NoOpRestLogger}.
+ * @return A new mock interface.
+ * @throws RuntimeException
+ * For testing conveniences, this method wraps all exceptions in a RuntimeException so that you can easily define mocks as reusable fields.
+ */
+ public static MockRest create(Class<?> c, boolean debug) throws RuntimeException {
try {
- return new MockRest(c);
+ return new MockRest(c, debug);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
-
+
/**
* Performs a REST request against the REST interface.
*
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
index d3811b2..b55aef8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
@@ -794,9 +794,9 @@ public class MockServletRequest implements HttpServletRequest, MockHttpRequest {
public String getPathInfo() {
if (pathInfo == null) {
pathInfo = getRequestURI();
- if (! isEmpty(contextPath))
+ if (isNotEmpty(contextPath))
pathInfo = pathInfo.substring(contextPath.length());
- if (! isEmpty(servletPath))
+ if (isNotEmpty(servletPath))
pathInfo = pathInfo.substring(servletPath.length());
}
return nullIfEmpty(urlDecode(pathInfo));
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/AnnotationUtils.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/AnnotationUtils.java
index 5c9cf72..625a258 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/AnnotationUtils.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/AnnotationUtils.java
@@ -47,7 +47,7 @@ public class AnnotationUtils {
return om
.appendSkipEmpty("_value", joinnl(a.value()))
.appendSkipEmpty("description", joinnl(a.description()))
- .appendSkipEmpty("required", a.required())
+ .appendSkipFalse("required", a.required())
.appendSkipEmpty("example", joinnl(a.example()))
.appendSkipEmpty("examples", joinnl(a.examples()))
.appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema()));
@@ -81,6 +81,7 @@ public class AnnotationUtils {
if (empty(a))
return om;
om = newMap(om);
+ a._enum();
return om
.appendSkipEmpty("_value", joinnl(a.value()))
.appendSkipEmpty("$ref", a.$ref())
@@ -90,18 +91,18 @@ public class AnnotationUtils {
.appendSkipEmpty("default", joinnl(a._default()))
.appendSkipEmpty("multipleOf", a.multipleOf())
.appendSkipEmpty("maximum", a.maximum())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
.appendSkipEmpty("minimum", a.minimum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
.appendSkipEmpty("pattern", a.pattern())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("maxProperties", a.maxProperties())
.appendSkipEmpty("minProperties", a.minProperties())
- .appendSkipEmpty("required", a.required())
+ .appendSkipFalse("required", a.required())
.appendSkipEmpty("enum", joinnl(a._enum()))
.appendSkipEmpty("type", a.type())
.appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
@@ -109,7 +110,7 @@ public class AnnotationUtils {
.appendSkipEmpty("properties", joinnl(a.properties()))
.appendSkipEmpty("additionalProperties", joinnl(a.additionalProperties()))
.appendSkipEmpty("discriminator", a.discriminator())
- .appendSkipEmpty("readOnly", a.readOnly())
+ .appendSkipFalse("readOnly", a.readOnly())
.appendSkipEmpty("xml", joinnl(a.xml()))
.appendSkipEmpty("externalDocs", merge(om.getObjectMap("externalDocs"), a.externalDocs()))
.appendSkipEmpty("example", joinnl(a.example()))
@@ -178,15 +179,51 @@ public class AnnotationUtils {
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("default", joinnl(a._default()))
- .appendSkipEmpty("enum", joinnl(a._enum()));
+ .appendSkipEmpty("enum", joinnl(a._enum()))
+ .appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
+ ;
+ }
+
+ /**
+ * Merges the contents of the specified annotation into the specified map.
+ *
+ * @param om The map to add the annotation values to.
+ * @param a The annotation.
+ * @return The same map with merged results, or a new map if the map was <jk>null</jk>.
+ */
+ public static ObjectMap merge(ObjectMap om, SubItems a) {
+ if (empty(a))
+ return om;
+ om = newMap(om);
+ return om
+ .appendSkipEmpty("_value", joinnl(a.value()))
+ .appendSkipEmpty("type", a.type())
+ .appendSkipEmpty("format", a.format())
+ .appendSkipEmpty("collectionFormat", a.collectionFormat())
+ .appendSkipEmpty("pattern", a.pattern())
+ .appendSkipEmpty("$ref", a.$ref())
+ .appendSkipEmpty("maximum", a.maximum())
+ .appendSkipEmpty("minimum", a.minimum())
+ .appendSkipEmpty("multipleOf", a.multipleOf())
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
+ .appendSkipEmpty("default", joinnl(a._default()))
+ .appendSkipEmpty("enum", joinnl(a._enum()))
+ .appendSkipEmpty("items", joinnl(a.items()))
+ ;
}
/**
@@ -210,13 +247,13 @@ public class AnnotationUtils {
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("default", joinnl(a._default()))
.appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
.appendSkipEmpty("default", joinnl(a._default()))
@@ -255,17 +292,17 @@ public class AnnotationUtils {
.appendSkipEmpty("_value", joinnl(a.api()))
.appendSkipEmpty("description", joinnl(a.description()))
.appendSkipEmpty("type", a.type())
+ .appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
.appendSkipEmpty("format", a.format())
.appendSkipEmpty("pattern", a.pattern())
.appendSkipEmpty("collectionFormat", a.collectionFormat())
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema()))
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
.appendSkipEmpty("enum", joinnl(a._enum()))
.appendSkipEmpty("example", joinnl(a.example()));
}
@@ -284,7 +321,7 @@ public class AnnotationUtils {
return om
.appendSkipEmpty("_value", joinnl(a.api()))
.appendSkipEmpty("description", joinnl(a.description()))
- .appendSkipEmpty("required", a.required())
+ .appendSkipFalse("required", a.required())
.appendSkipEmpty("type", a.type())
.appendSkipEmpty("format", a.format())
.appendSkipEmpty("pattern", a.pattern())
@@ -292,15 +329,14 @@ public class AnnotationUtils {
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("allowEmptyValue", a.allowEmptyValue())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
- .appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema()))
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("allowEmptyValue", a.allowEmptyValue())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("default", joinnl(a._default()))
.appendSkipEmpty("enum", joinnl(a._enum()))
.appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
@@ -321,7 +357,7 @@ public class AnnotationUtils {
return om
.appendSkipEmpty("_value", joinnl(a.api()))
.appendSkipEmpty("description", joinnl(a.description()))
- .appendSkipEmpty("required", a.required())
+ .appendSkipFalse("required", a.required())
.appendSkipEmpty("type", a.type())
.appendSkipEmpty("format", a.format())
.appendSkipEmpty("pattern", a.pattern())
@@ -329,14 +365,13 @@ public class AnnotationUtils {
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
- .appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema()))
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("default", joinnl(a._default()))
.appendSkipEmpty("enum", joinnl(a._enum()))
.appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
@@ -357,7 +392,7 @@ public class AnnotationUtils {
return om
.appendSkipEmpty("_value", joinnl(a.api()))
.appendSkipEmpty("description", joinnl(a.description()))
- .appendSkipEmpty("required", a.required())
+ .appendSkipFalse("required", a.required())
.appendSkipEmpty("type", a.type())
.appendSkipEmpty("format", a.format())
.appendSkipEmpty("pattern", a.pattern())
@@ -365,15 +400,14 @@ public class AnnotationUtils {
.appendSkipEmpty("maximum", a.maximum())
.appendSkipEmpty("minimum", a.minimum())
.appendSkipEmpty("multipleOf", a.multipleOf())
- .appendSkipEmpty("maxLength", a.maxLength())
- .appendSkipEmpty("minLength", a.minLength())
- .appendSkipEmpty("maxItems", a.maxItems())
- .appendSkipEmpty("minItems", a.minItems())
- .appendSkipEmpty("allowEmptyValue", a.allowEmptyValue())
- .appendSkipEmpty("exclusiveMaximum", a.exclusiveMaximum())
- .appendSkipEmpty("exclusiveMinimum", a.exclusiveMinimum())
- .appendSkipEmpty("uniqueItems", a.uniqueItems())
- .appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema()))
+ .appendSkipMinusOne("maxLength", a.maxLength())
+ .appendSkipMinusOne("minLength", a.minLength())
+ .appendSkipMinusOne("maxItems", a.maxItems())
+ .appendSkipMinusOne("minItems", a.minItems())
+ .appendSkipFalse("allowEmptyValue", a.allowEmptyValue())
+ .appendSkipFalse("exclusiveMaximum", a.exclusiveMaximum())
+ .appendSkipFalse("exclusiveMinimum", a.exclusiveMinimum())
+ .appendSkipFalse("uniqueItems", a.uniqueItems())
.appendSkipEmpty("default", joinnl(a._default()))
.appendSkipEmpty("enum", joinnl(a._enum()))
.appendSkipEmpty("items", merge(om.getObjectMap("items"), a.items()))
@@ -395,11 +429,9 @@ public class AnnotationUtils {
return true;
return
empty(a.description(), a._default(), a.example(), a.api())
- && empty(
- a.name(), a.value(), a.required(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf(), a.maxLength(), a.minLength(),
- a.maxItems(), a.minItems(), a.allowEmptyValue(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems()
- )
- && empty(a.schema())
+ && empty(a.name(), a.value(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf())
+ && empty(a.allowEmptyValue(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.required(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
&& empty(a.items());
}
@@ -414,11 +446,9 @@ public class AnnotationUtils {
return true;
return
empty(a.description(), a._default(), a._enum(), a.example(), a.api())
- && empty(
- a.name(), a.value(), a.required(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf(), a.maxLength(),
- a.minLength(), a.maxItems(), a.minItems(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems()
- )
- && empty(a.schema())
+ && empty(a.name(), a.value(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf())
+ && empty(a.exclusiveMaximum(), a.exclusiveMinimum(), a.required(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
&& empty(a.items());
}
@@ -433,11 +463,9 @@ public class AnnotationUtils {
return true;
return
empty(a.description(), a._default(), a._enum(), a.example(), a.api())
- && empty(
- a.name(), a.value(), a.required(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf(), a.maxLength(),
- a.minLength(), a.maxItems(), a.minItems(), a.allowEmptyValue(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems()
- )
- && empty(a.schema())
+ && empty(a.name(), a.value(), a.type(), a.format(), a.pattern(), a.collectionFormat(), a.maximum(), a.minimum(), a.multipleOf())
+ && empty(a.allowEmptyValue(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.required(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
&& empty(a.items());
}
@@ -468,10 +496,9 @@ public class AnnotationUtils {
return true;
return
empty(a.description(), a._default(), a._enum(), a.example(), a.api())
- && empty(
- a.name(), a.value(), a.type(), a.format(), a.collectionFormat(), a.$ref(), a.maximum(), a.minimum(), a.multipleOf(),
- a.maxLength(), a.minLength(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems()
- )
+ && empty(a.name(), a.value(), a.type(), a.format(), a.collectionFormat(), a.$ref(), a.maximum(), a.minimum(), a.multipleOf())
+ && empty(a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
&& empty(a.items());
}
@@ -498,15 +525,10 @@ public class AnnotationUtils {
if (a == null)
return true;
return
- empty(
- a.value(), a.description(), a._default(), a._enum(), a.allOf(), a.properties(), a.additionalProperties(), a.xml(), a.example(), a.examples()
- )
- && empty(
- a.$ref(), a.format(), a.title(), a.multipleOf(), a.maximum(), a.exclusiveMaximum(), a.minimum(), a.exclusiveMinimum(), a.maxLength(),
- a.minLength(), a.pattern(), a.maxItems(), a.minItems(), a.uniqueItems(), a.maxProperties(), a.minProperties(), a.required(),
- a.type(), a.discriminator(), a.readOnly()
- )
- && ! a.ignore()
+ empty(a.value(), a.description(), a._default(), a._enum(), a.allOf(), a.properties(), a.additionalProperties(), a.xml(), a.example(), a.examples())
+ && empty(a.$ref(), a.format(), a.title(), a.multipleOf(), a.maximum(), a.minimum(), a.pattern(), a.maxProperties(), a.minProperties(), a.type(), a.discriminator())
+ && empty(a.ignore(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.readOnly(), a.required(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
&& empty(a.items())
&& empty(a.externalDocs());
}
@@ -579,10 +601,26 @@ public class AnnotationUtils {
return true;
return
empty(a.value(), a._default(), a._enum())
- && empty(
- a.type(), a.format(), a.collectionFormat(), a.pattern(), a.$ref(), a.maximum(), a.minimum(), a.multipleOf(),
- a.maxLength(), a.minLength(), a.maxItems(), a.minItems(), a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems()
- );
+ && empty(a.type(), a.format(), a.collectionFormat(), a.pattern(), a.$ref(), a.maximum(), a.minimum(), a.multipleOf())
+ && empty(a.exclusiveMaximum(), a.exclusiveMinimum(), a.uniqueItems())
+ && empty(a.maxLength(), a.minLength(), a.maxItems(), a.minItems())
... 2536 lines suppressed ...