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 b29fc42 Javadocs. b29fc42 is described below commit b29fc42e42d1ec43188b3f2350088e78155a817d Author: JamesBognar <jamesbog...@apache.org> AuthorDate: Mon Jul 2 16:42:49 2018 -0400 Javadocs. --- .../juneau/httppart/HttpPartSchemaTest_Body.java | 8 +- .../httppart/HttpPartSchemaTest_FormData.java | 8 +- .../juneau/httppart/HttpPartSchemaTest_Header.java | 8 +- .../juneau/httppart/HttpPartSchemaTest_Path.java | 8 +- .../juneau/httppart/HttpPartSchemaTest_Query.java | 8 +- .../httppart/HttpPartSchemaTest_Response.java | 8 +- .../HttpPartSchemaTest_ResponseHeader.java | 8 +- .../juneau/httppart/OpenApiPartParserTest.java | 439 +++++++++++++++++++++ .../main/java/org/apache/juneau/BeanSession.java | 51 ++- .../src/main/java/org/apache/juneau/ClassMeta.java | 58 ++- .../org/apache/juneau/httppart/HttpPartSchema.java | 141 ++++++- .../apache/juneau/httppart/OpenApiPartParser.java | 84 +++- .../org/apache/juneau/internal/StringUtils.java | 19 +- 13 files changed, 805 insertions(+), 43 deletions(-) diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java index 4318149..0a7242e 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Body.java @@ -47,7 +47,7 @@ public class HttpPartSchemaTest_Body { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A02.class).noValidate().build(); assertTrue(s.getRequired()); assertObjectEquals("{description:'b1\\nb2',example:'f1',required:true,schema:{'$ref':'c1'},_value:'{g1:true}'}", s.getApi()); } @@ -68,7 +68,7 @@ public class HttpPartSchemaTest_Body { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertTrue(s.getRequired()); assertObjectEquals("{description:'b1\\nb2',example:'f1',required:true,schema:{'$ref':'c1'},_value:'{g1:true}'}", s.getApi()); } @@ -89,7 +89,7 @@ public class HttpPartSchemaTest_Body { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A04.class.getMethod("a", A02.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A04.class.getMethod("a", A02.class), 0).noValidate().build(); assertNull(s.getRequired()); assertObjectEquals("{description:'b3\\nb3',example:'f2',schema:{'$ref':'c3'},_value:'{g2:true}'}", s.getApi()); } @@ -173,7 +173,7 @@ public class HttpPartSchemaTest_Body { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Body.class, A05.class).noValidate().build(); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java index c492f3f..d8c125a 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_FormData.java @@ -75,7 +75,7 @@ public class HttpPartSchemaTest_FormData { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A02.class).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -132,7 +132,7 @@ public class HttpPartSchemaTest_FormData { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -189,7 +189,7 @@ public class HttpPartSchemaTest_FormData { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A04.class.getMethod("a", A01.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A04.class.getMethod("a", A01.class), 0).noValidate().build(); assertEquals("y", s.getName()); assertEquals(HttpPartSchema.Type.INTEGER, s.getType()); assertEquals(HttpPartSchema.Format.INT64, s.getFormat()); @@ -291,7 +291,7 @@ public class HttpPartSchemaTest_FormData { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(FormData.class, A05.class).noValidate().build(); assertEquals("x", s.getName()); HttpPartSchema items = s.getItems(); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java index aab298f..eb4bc39 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Header.java @@ -75,7 +75,7 @@ public class HttpPartSchemaTest_Header { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A02.class).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -132,7 +132,7 @@ public class HttpPartSchemaTest_Header { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -189,7 +189,7 @@ public class HttpPartSchemaTest_Header { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A04.class.getMethod("a", A01.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A04.class.getMethod("a", A01.class), 0).noValidate().build(); assertEquals("y", s.getName()); assertEquals(HttpPartSchema.Type.INTEGER, s.getType()); assertEquals(HttpPartSchema.Format.INT64, s.getFormat()); @@ -291,7 +291,7 @@ public class HttpPartSchemaTest_Header { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Header.class, A05.class).noValidate().build(); assertEquals("x", s.getName()); HttpPartSchema items = s.getItems(); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java index ee0f017..2f855e0 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Path.java @@ -69,7 +69,7 @@ public class HttpPartSchemaTest_Path { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A02.class).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -114,7 +114,7 @@ public class HttpPartSchemaTest_Path { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -159,7 +159,7 @@ public class HttpPartSchemaTest_Path { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A04.class.getMethod("a", A01.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A04.class.getMethod("a", A01.class), 0).noValidate().build(); assertEquals("y", s.getName()); assertEquals(HttpPartSchema.Type.INTEGER, s.getType()); assertEquals(HttpPartSchema.Format.INT64, s.getFormat()); @@ -255,7 +255,7 @@ public class HttpPartSchemaTest_Path { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Path.class, A05.class).noValidate().build(); assertEquals("x", s.getName()); HttpPartSchema items = s.getItems(); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java index d45eeb8..0afebbc 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Query.java @@ -75,7 +75,7 @@ public class HttpPartSchemaTest_Query { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A02.class).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -132,7 +132,7 @@ public class HttpPartSchemaTest_Query { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -189,7 +189,7 @@ public class HttpPartSchemaTest_Query { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A04.class.getMethod("a", A01.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A04.class.getMethod("a", A01.class), 0).noValidate().build(); assertEquals("y", s.getName()); assertEquals(HttpPartSchema.Type.INTEGER, s.getType()); assertEquals(HttpPartSchema.Format.INT64, s.getFormat()); @@ -291,7 +291,7 @@ public class HttpPartSchemaTest_Query { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Query.class, A05.class).noValidate().build(); assertEquals("x", s.getName()); HttpPartSchema items = s.getItems(); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Response.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Response.java index 8a94af9..52688ad 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Response.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_Response.java @@ -46,7 +46,7 @@ public class HttpPartSchemaTest_Response { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A02.class).noValidate().build(); assertObjectEquals("{description:'b1\\nb2',example:'f1',schema:{'$ref':'c1'},_value:'{g1:true}'}", s.getApi()); } @@ -65,7 +65,7 @@ public class HttpPartSchemaTest_Response { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertObjectEquals("{description:'b1\\nb2',example:'f1',schema:{'$ref':'c1'},_value:'{g1:true}'}", s.getApi()); } @@ -84,7 +84,7 @@ public class HttpPartSchemaTest_Response { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A04.class.getMethod("a", A02.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A04.class.getMethod("a", A02.class), 0).noValidate().build(); assertObjectEquals("{description:'b3\\nb3',example:'f2',schema:{'$ref':'c3'},_value:'{g2:true}'}", s.getApi()); } @@ -167,7 +167,7 @@ public class HttpPartSchemaTest_Response { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(Response.class, A05.class).noValidate().build(); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java index 485f503..ef1ec7c 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/HttpPartSchemaTest_ResponseHeader.java @@ -73,7 +73,7 @@ public class HttpPartSchemaTest_ResponseHeader { @Test public void a02_basic_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A02.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A02.class).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -126,7 +126,7 @@ public class HttpPartSchemaTest_ResponseHeader { @Test public void a03_basic_onParameter() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A03.class.getMethod("a", String.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A03.class.getMethod("a", String.class), 0).noValidate().build(); assertEquals("x", s.getName()); assertEquals(HttpPartSchema.Type.NUMBER, s.getType()); assertEquals(HttpPartSchema.Format.INT32, s.getFormat()); @@ -179,7 +179,7 @@ public class HttpPartSchemaTest_ResponseHeader { @Test public void a04_basic_onParameterAndClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A04.class.getMethod("a", A01.class), 0).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A04.class.getMethod("a", A01.class), 0).noValidate().build(); assertEquals("y", s.getName()); assertEquals(HttpPartSchema.Type.INTEGER, s.getType()); assertEquals(HttpPartSchema.Format.INT64, s.getFormat()); @@ -281,7 +281,7 @@ public class HttpPartSchemaTest_ResponseHeader { @Test public void a05_basic_nestedItems_onClass() throws Exception { - HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A05.class).noValidate(true).build(); + HttpPartSchema s = HttpPartSchema.create().apply(ResponseHeader.class, A05.class).noValidate().build(); assertEquals("x", s.getName()); HttpPartSchema items = s.getItems(); diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java new file mode 100644 index 0000000..44d668b --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java @@ -0,0 +1,439 @@ +// *************************************************************************************************************************** +// * 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 org.junit.Assert.*; + +import java.io.*; +import java.util.*; + +import org.apache.juneau.internal.*; +import org.junit.*; + +public class OpenApiPartParserTest { + + static OpenApiPartParser p = OpenApiPartParser.DEFAULT; + + //----------------------------------------------------------------------------------------------------------------- + // Input validations + //----------------------------------------------------------------------------------------------------------------- + + @Test + public void a01_inputValidations_nullInput() throws Exception { + HttpPartSchema s = HttpPartSchema.create().build(); + assertNull(p.parse(s, null, String.class)); + + s = HttpPartSchema.create().required(false).build(); + assertNull(p.parse(s, null, String.class)); + + s = HttpPartSchema.create().required().build(); + try { + p.parse(s, null, String.class); + fail(); + } catch (Exception e) { + assertEquals("No value specified.", e.getMessage()); + } + + s = HttpPartSchema.create().required(true).build(); + try { + p.parse(s, null, String.class); + fail(); + } catch (Exception e) { + assertEquals("No value specified.", e.getMessage()); + } + } + + @Test + public void a02_inputValidations_emptyInput() throws Exception { + + HttpPartSchema s = HttpPartSchema.create().allowEmptyValue().build(); + assertEquals("", p.parse(s, "", String.class)); + + s = HttpPartSchema.create().allowEmptyValue().build(); + assertEquals("", p.parse(s, "", String.class)); + + s = HttpPartSchema.create().allowEmptyValue(false).build(); + try { + p.parse(s, "", String.class); + fail(); + } catch (Exception e) { + assertEquals("Empty value not allowed.", e.getMessage()); + } + + try { + p.parse(s, "", String.class); + fail(); + } catch (Exception e) { + assertEquals("Empty value not allowed.", e.getMessage()); + } + + assertEquals(" ", p.parse(s, " ", String.class)); + } + + @Test + public void a03_inputValidations_pattern() throws Exception { + HttpPartSchema s = HttpPartSchema.create().pattern("x.*").allowEmptyValue().build(); + assertEquals("x", p.parse(s, "x", String.class)); + assertEquals("xx", p.parse(s, "xx", String.class)); + assertEquals(null, p.parse(s, null, String.class)); + + try { + p.parse(s, "y", String.class); + fail(); + } catch (Exception e) { + assertEquals("Value does not match expected pattern. Must match pattern: x.*", e.getMessage()); + } + + try { + p.parse(s, "", String.class); + fail(); + } catch (Exception e) { + assertEquals("Value does not match expected pattern. Must match pattern: x.*", e.getMessage()); + } + + // Blank/null patterns are ignored. + s = HttpPartSchema.create().pattern("").allowEmptyValue().build(); + assertEquals("x", p.parse(s, "x", String.class)); + s = HttpPartSchema.create().pattern(null).allowEmptyValue().build(); + assertEquals("x", p.parse(s, "x", String.class)); + } + + @Test + public void a04_inputValidations_enum() throws Exception { + HttpPartSchema s = HttpPartSchema.create()._enum("foo").allowEmptyValue().build(); + + assertEquals("foo", p.parse(s, "foo", String.class)); + assertEquals(null, p.parse(s, null, String.class)); + + try { + p.parse(s, "bar", String.class); + fail(); + } catch (Exception e) { + assertEquals("Value does not match one of the expected values. Must be one of the following: ['foo']", e.getMessage()); + } + + try { + p.parse(s, "", String.class); + fail(); + } catch (Exception e) { + assertEquals("Value does not match one of the expected values. Must be one of the following: ['foo']", e.getMessage()); + } + + s = HttpPartSchema.create()._enum((Set<String>)null).build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + s = HttpPartSchema.create()._enum((Set<String>)null).allowEmptyValue().build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + + s = HttpPartSchema.create()._enum("foo","foo").build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + } + + @Test + public void a05_inputValidations_minMaxLength() throws Exception { + HttpPartSchema s = HttpPartSchema.create().minLength(1l).maxLength(2l).allowEmptyValue().build(); + + assertEquals(null, p.parse(s, null, String.class)); + assertEquals("1", p.parse(s, "1", String.class)); + assertEquals("12", p.parse(s, "12", String.class)); + + try { + p.parse(s, "", String.class); + fail(); + } catch (Exception e) { + assertEquals("Minimum length of value not met.", e.getMessage()); + } + + try { + p.parse(s, "123", String.class); + fail(); + } catch (Exception e) { + assertEquals("Maximum length of value exceeded.", e.getMessage()); + } + + try { + s = HttpPartSchema.create().minLength(2l).maxLength(1l).build(); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("maxLength cannot be less than minLength.")); + } + + try { + s = HttpPartSchema.create().minLength(-1l).build(); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("minLength cannot be less than zero.")); + } + + try { + s = HttpPartSchema.create().maxLength(-1l).build(); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("maxLength cannot be less than zero.")); + } + } + + //----------------------------------------------------------------------------------------------------------------- + // Primitive defaults + //----------------------------------------------------------------------------------------------------------------- + + @Test + public void b01_primitiveDefaults() throws Exception { + + assertEquals(null, p.parse(null, null, Boolean.class)); + assertEquals(false, p.parse(null, null, boolean.class)); + assertEquals(null, p.parse(null, null, Character.class)); + assertEquals("\0", p.parse(null, null, char.class).toString()); + assertEquals(null, p.parse(null, null, Short.class)); + assertEquals(0, p.parse(null, null, short.class).intValue()); + assertEquals(null, p.parse(null, null, Integer.class)); + assertEquals(0, p.parse(null, null, int.class).intValue()); + assertEquals(null, p.parse(null, null, Long.class)); + assertEquals(0, p.parse(null, null, long.class).intValue()); + assertEquals(null, p.parse(null, null, Float.class)); + assertEquals(0, p.parse(null, null, float.class).intValue()); + assertEquals(null, p.parse(null, null, Double.class)); + assertEquals(0, p.parse(null, null, double.class).intValue()); + assertEquals(null, p.parse(null, null, Byte.class)); + assertEquals(0, p.parse(null, null, byte.class).intValue()); + } + + //----------------------------------------------------------------------------------------------------------------- + // type = string + //----------------------------------------------------------------------------------------------------------------- + + @Test + public void c01_stringType_simple() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + } + + @Test + public void c02_stringType_default() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string")._default("x").build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + assertEquals("x", p.parse(s, null, String.class)); + } + + public static class C3 { + private String f; + public C3(byte[] b) { + f = new String(b); + } + @Override + public String toString() { + return f; + } + } + + @Test + public void c03_stringType_byteFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("byte").build(); + String in = StringUtils.base64Encode("foo".getBytes()); + assertEquals("foo", p.parse(s, in, String.class)); + assertEquals("foo", IOUtils.read(p.parse(s, in, InputStream.class))); + assertEquals("foo", IOUtils.read(p.parse(s, in, Reader.class))); + assertEquals("foo", p.parse(s, in, C3.class).toString()); + } + + @Test + public void c04_stringType_binaryFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("binary").build(); + String in = StringUtils.toHex("foo".getBytes()); + assertEquals("foo", p.parse(s, in, String.class)); + assertEquals("foo", IOUtils.read(p.parse(s, in, InputStream.class))); + assertEquals("foo", IOUtils.read(p.parse(s, in, Reader.class))); + assertEquals("foo", p.parse(s, in, C3.class).toString()); + } + + @Test + public void c05_stringType_binarySpacedFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("binary-spaced").build(); + String in = StringUtils.toSpacedHex("foo".getBytes()); + assertEquals("foo", p.parse(s, in, String.class)); + assertEquals("foo", IOUtils.read(p.parse(s, in, InputStream.class))); + assertEquals("foo", IOUtils.read(p.parse(s, in, Reader.class))); + assertEquals("foo", p.parse(s, in, C3.class).toString()); + } + + @Test + public void c06_stringType_dateFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("date").build(); + String in = "2012-12-21"; + assertTrue(p.parse(s, in, String.class).contains("2012")); + assertTrue(p.parse(s, in, Date.class).toString().contains("2012")); + assertEquals(2012, p.parse(s, in, Calendar.class).get(Calendar.YEAR)); + assertEquals(2012, p.parse(s, in, GregorianCalendar.class).get(Calendar.YEAR)); + } + + @Test + public void c07_stringType_dateTimeFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("date-time").build(); + String in = "2012-12-21T12:34:56.789"; + assertTrue(p.parse(s, in, String.class).contains("2012")); + assertTrue(p.parse(s, in, Date.class).toString().contains("2012")); + assertEquals(2012, p.parse(s, in, Calendar.class).get(Calendar.YEAR)); + assertEquals(2012, p.parse(s, in, GregorianCalendar.class).get(Calendar.YEAR)); + } + + public static class C8 { + private String f; + public C8(String s) { + f = s; + } + @Override + public String toString() { + return f; + } + } + + @Test + public void c08_stringType_uonFormat() throws Exception { + HttpPartSchema s = HttpPartSchema.create().type("string").format("uon").build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + assertEquals("foo", p.parse(s, "'foo'", String.class)); + assertEquals("foo", p.parse(s, "'foo'", C8.class).toString()); + assertEquals("C8", p.parse(s, "'foo'", C8.class).getClass().getSimpleName()); + // UonPartParserTest should handle all other cases. + } + + @Test + public void c08_stringType_noneFormat() throws Exception { + // If no format is specified, then we should transform directly from a string. + HttpPartSchema s = HttpPartSchema.create().type("string").build(); + assertEquals("foo", p.parse(s, "foo", String.class)); + assertEquals("'foo'", p.parse(s, "'foo'", String.class)); + assertEquals("foo", p.parse(s, "foo", C8.class).toString()); + assertEquals("C8", p.parse(s, "foo", C8.class).getClass().getSimpleName()); + } + + //----------------------------------------------------------------------------------------------------------------- + // type = array + //----------------------------------------------------------------------------------------------------------------- + +// 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); +//} + + //----------------------------------------------------------------------------------------------------------------- + // type = boolean + //----------------------------------------------------------------------------------------------------------------- + +// case BOOLEAN: { +// if (type.isObject()) +// type = (ClassMeta<T>)getClassMeta(Boolean.class); +// return super.parse(partType, schema, in, type); +//} + + //----------------------------------------------------------------------------------------------------------------- + // type = integer + //----------------------------------------------------------------------------------------------------------------- + + +// 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); +// } + + //----------------------------------------------------------------------------------------------------------------- + // type = number + //----------------------------------------------------------------------------------------------------------------- +// 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); +// } + + //----------------------------------------------------------------------------------------------------------------- + // type = object + //----------------------------------------------------------------------------------------------------------------- +// case OBJECT: { +// if (type.isObject()) +// type = (ClassMeta<T>)getClassMeta(ObjectMap.class); +// switch (schema.getFormat()) { +// default: +// return super.parse(partType, schema, in, type); +// } +// } + + //----------------------------------------------------------------------------------------------------------------- + // type = file + //----------------------------------------------------------------------------------------------------------------- +// case FILE: { +// throw new ParseException("File part not supported."); +// } + + //----------------------------------------------------------------------------------------------------------------- + // type = none + //----------------------------------------------------------------------------------------------------------------- +// case NONE: { +// throw new ParseException("Invalid type."); +// } +// } +// } + +} \ No newline at end of file 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 1f42e35..8c7fba9 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 @@ -17,8 +17,10 @@ import static org.apache.juneau.internal.ClassUtils.*; import static org.apache.juneau.internal.StringUtils.*; import static org.apache.juneau.internal.ThrowableUtils.*; +import java.io.*; import java.lang.reflect.*; import java.util.*; +import java.util.Date; import java.util.concurrent.atomic.*; import org.apache.juneau.http.*; @@ -585,7 +587,9 @@ public class BeanSession extends Session { } if (type.isString()) { - if (vt.isMapOrBean() || vt.isCollectionOrArray()) { + if (vt.isByteArray()) { + return (T) new String((byte[])value); + } else if (vt.isMapOrBean() || vt.isCollectionOrArray()) { if (JsonSerializer.DEFAULT_LAX != null) return (T)JsonSerializer.DEFAULT_LAX.serialize(value); } else if (vt.isClass()) { @@ -630,12 +634,57 @@ public class BeanSession extends Session { return newBeanMap(tc).load((Map<?,?>) value).getBean(); } + if (type.isInputStream()) { + if (vt.isByteArray()) { + byte[] b = (byte[])value; + return (T) new ByteArrayInputStream(b, 0, b.length); + } + byte[] b = value.toString().getBytes(); + return (T)new ByteArrayInputStream(b, 0, b.length); + } + + if (type.isReader()) { + if (vt.isByteArray()) { + byte[] b = (byte[])value; + return (T) new StringReader(new String(b)); + } + return (T)new StringReader(value.toString()); + } + if (type.canCreateNewInstanceFromNumber(outer) && value instanceof Number) return type.newInstanceFromNumber(this, outer, (Number)value); if (type.canCreateNewInstanceFromString(outer)) return type.newInstanceFromString(outer, value.toString()); + if (type.isCalendar()) { + if (vt.isCalendar()) { + Calendar c = (Calendar)value; + if (value instanceof GregorianCalendar) { + GregorianCalendar c2 = new GregorianCalendar(c.getTimeZone()); + c2.setTime(c.getTime()); + return (T)c2; + } + } + if (vt.isDate()) { + Date d = (Date)value; + if (value instanceof GregorianCalendar) { + GregorianCalendar c2 = new GregorianCalendar(TimeZone.getDefault()); + c2.setTime(d); + return (T)c2; + } + } + } + + if (type.isDate() && type.getInnerClass() == Date.class) { + if (vt.isCalendar()) + return (T)((Calendar)value).getTime(); + } + + Transform t = type.getTransform(value.getClass()); + if (t != null) + return (T) t.transform(value); + if (type.isBean()) return newBeanMap(type.getInnerClass()).load(value.toString()).getBean(); diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java index e16bee9..0a4b1c6 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java @@ -119,6 +119,7 @@ public final class ClassMeta<T> implements Type { private final BeanRegistry beanRegistry; // The bean registry of this class meta (if it has one). private final ClassMeta<?>[] args; // Arg types if this is an array of args. private final Object example; // Example object. + private final Map<Class<?>,Transform<?,T>> transforms = new ConcurrentHashMap<>(); private final Transform<Reader,T> readerTransform; private final Transform<InputStream,T> inputStreamTransform; private final Transform<String,T> stringTransform; @@ -1184,6 +1185,15 @@ public final class ClassMeta<T> implements Type { } /** + * Returns <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>. + * + * @return <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>. + */ + public boolean isByteArray() { + return cc == ARRAY && this.innerClass == byte[].class; + } + + /** * Returns <jk>true</jk> if this class is {@link Class}. * * @return <jk>true</jk> if this class is {@link Class}. @@ -1369,11 +1379,29 @@ public final class ClassMeta<T> implements Type { * * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar}. */ - public boolean isDate() { + public boolean isDateOrCalendar() { return cc == DATE; } /** + * Returns <jk>true</jk> if this class is a {@link Date}. + * + * @return <jk>true</jk> if this class is a {@link Date}. + */ + public boolean isDate() { + return cc == DATE && ClassUtils.isParentClass(Date.class, innerClass); + } + + /** + * Returns <jk>true</jk> if this class is a {@link Calendar}. + * + * @return <jk>true</jk> if this class is a {@link Calendar}. + */ + public boolean isCalendar() { + return cc == DATE && ClassUtils.isParentClass(Calendar.class, innerClass); + } + + /** * Returns <jk>true</jk> if this class is a {@link URI} or {@link URL}. * * @return <jk>true</jk> if this class is a {@link URI} or {@link URL}. @@ -2053,7 +2081,7 @@ public final class ClassMeta<T> implements Type { * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader. */ public boolean hasReaderTransform() { - return readerTransform != null; + return getTransform(Reader.class) != null; } /** @@ -2062,7 +2090,7 @@ public final class ClassMeta<T> implements Type { * @return The transform, or <jk>null</jk> if no such transform exists. */ public Transform<Reader,T> getReaderTransform() { - return readerTransform; + return getTransform(Reader.class); } /** @@ -2071,7 +2099,7 @@ public final class ClassMeta<T> implements Type { * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream. */ public boolean hasInputStreamTransform() { - return inputStreamTransform != null; + return getTransform(InputStream.class) != null; } /** @@ -2080,7 +2108,7 @@ public final class ClassMeta<T> implements Type { * @return The transform, or <jk>null</jk> if no such transform exists. */ public Transform<InputStream,T> getInputStreamTransform() { - return inputStreamTransform; + return getTransform(InputStream.class); } /** @@ -2100,4 +2128,24 @@ public final class ClassMeta<T> implements Type { public Transform<String,T> getStringTransform() { return stringTransform; } + + /** + * Returns the transform for this class for creating instances from other object types. + * + * @param c The transform-from class. + * @return The transform, or <jk>null</jk> if no such transform exists. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public <I> Transform<I,T> getTransform(Class<I> c) { + Transform t = transforms.get(c); + if (t == TransformCache.NULL) + return null; + if (t == null) { + t = TransformCache.get(c, innerClass); + if (t == null) + t = TransformCache.NULL; + transforms.put(c, t); + } + return t == TransformCache.NULL ? null : t; + } } 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 index 8e4a65f..66619c4 100644 --- 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 @@ -268,7 +268,7 @@ public class HttpPartSchema { 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); + invalidFormat = ! format.isOneOf(Format.BYTE, Format.BINARY, Format.BINARY_SPACED, Format.DATE, Format.DATE_TIME, Format.PASSWORD, Format.UON, Format.NONE); break; } case ARRAY: { @@ -375,6 +375,26 @@ public class HttpPartSchema { 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 (minLength != null && maxLength != null && maxLength < minLength) + errors.add("maxLength cannot be less than minLength."); + if (minimum != null && maximum != null && maximum.doubleValue() < minimum.doubleValue()) + errors.add("maximum cannot be less than minimum."); + if (minItems != null && maxItems != null && maxItems < minItems) + errors.add("maxItems cannot be less than minItems."); + if (minProperties != null && maxProperties != null && maxProperties < minProperties) + errors.add("maxProperties cannot be less than minProperties."); + if (minLength != null && minLength < 0) + errors.add("minLength cannot be less than zero."); + if (maxLength != null && maxLength < 0) + errors.add("maxLength cannot be less than zero."); + if (minItems != null && minItems < 0) + errors.add("minItems cannot be less than zero."); + if (maxItems != null && maxItems < 0) + errors.add("maxItems cannot be less than zero."); + if (minProperties != null && minProperties < 0) + errors.add("minProperties cannot be less than zero."); + if (maxProperties != null && maxProperties < 0) + errors.add("maxProperties cannot be less than zero."); if (! errors.isEmpty()) throw new ContextRuntimeException("Schema specification errors: \n\t" + join(errors, "\n\t")); @@ -819,6 +839,18 @@ public class HttpPartSchema { } /** + * <mk>required</mk> field. + * + * <p> + * Shortcut for calling <code>required(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder required() { + return required(true); + } + + /** * <mk>type</mk> field. * * <p> @@ -918,6 +950,10 @@ public class HttpPartSchema { * <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-spaced"</js> - Hexadecimal encoded octets, spaced (e.g. <js>"00 FF"</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> @@ -987,6 +1023,18 @@ public class HttpPartSchema { } /** + * <mk>allowEmptyValue</mk> field. + * + * <p> + * Shortcut for calling <code>allowEmptyValue(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder allowEmptyValue() { + return allowEmptyValue(true); + } + + /** * <mk>items</mk> field. * * <p> @@ -1170,6 +1218,18 @@ public class HttpPartSchema { } /** + * <mk>exclusiveMaximum</mk> field. + * + * <p> + * Shortcut for calling <code>exclusiveMaximum(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder exclusiveMaximum() { + return exclusiveMaximum(true); + } + + /** * <mk>minimum</mk> field. * * <p> @@ -1229,6 +1289,18 @@ public class HttpPartSchema { } /** + * <mk>exclusiveMinimum</mk> field. + * + * <p> + * Shortcut for calling <code>exclusiveMinimum(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder exclusiveMinimum() { + return exclusiveMinimum(true); + } + + /** * <mk>maxLength</mk> field. * * <p> @@ -1413,6 +1485,18 @@ public class HttpPartSchema { } /** + * <mk>uniqueItems</mk> field. + * + * <p> + * Shortcut for calling <code>uniqueItems(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder uniqueItems() { + return uniqueItems(true); + } + + /** * Identifies whether an item should be skipped if it's empty. * * @param value @@ -1427,6 +1511,18 @@ public class HttpPartSchema { } /** + * Identifies whether an item should be skipped if it's empty. + * + * <p> + * Shortcut for calling <code>skipIfEmpty(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder skipIfEmpty() { + return skipIfEmpty(true); + } + + /** * <mk>enum</mk> field. * * <p> @@ -1443,16 +1539,31 @@ public class HttpPartSchema { * * @param value * The new value for this property. - * <br>Ignored if value is <jk>null</jk>. + * <br>Ignored if value is <jk>null</jk> or an empty set. * @return This object (for method chaining). */ public Builder _enum(Set<String> value) { - if (value != null) + if (value != null && ! value.isEmpty()) this._enum = value; return this; } /** + * <mk>_enum</mk> field. + * + * <p> + * Same as {@link #_enum(Set)} but takes in a var-args array. + * + * @param values + * The new values for this property. + * <br>Ignored if value is empty. + * @return This object (for method chaining). + */ + public Builder _enum(String...values) { + return _enum(new ASet<String>().appendAll(values)); + } + + /** * <mk>multipleOf</mk> field. * * <p> @@ -1593,13 +1704,26 @@ public class HttpPartSchema { /** * Disables Swagger schema usage validation checking. * - * @param noValidate Specify <jk>true</jk> to prevent {@link ContextRuntimeException} from being thrown if invalid Swagger usage was detected. + * @param value Specify <jk>true</jk> to prevent {@link ContextRuntimeException} from being thrown if invalid Swagger usage was detected. * @return This object (for method chaining). */ - public Builder noValidate(boolean noValidate) { - this.noValidate = noValidate; + public Builder noValidate(Boolean value) { + if (value != null) + this.noValidate = value; return this; } + + /** + * Disables Swagger schema usage validation checking. + * + * <p> + * Shortcut for calling <code>noValidate(<jk>true</jk>);</code>. + * + * @return This object (for method chaining). + */ + public Builder noValidate() { + return noValidate(true); + } } /** @@ -1744,6 +1868,11 @@ public class HttpPartSchema { BINARY, /** + * Spaced-separated hexadecimal encoded octets (e.g. <js>"00 FF"</js>). + */ + BINARY_SPACED, + + /** * An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>. */ DATE, diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartParser.java index 90532ba..70e7cc5 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartParser.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartParser.java @@ -14,12 +14,71 @@ package org.apache.juneau.httppart; import static org.apache.juneau.internal.StringUtils.*; +import java.io.*; +import java.util.*; + import org.apache.juneau.*; import org.apache.juneau.internal.*; import org.apache.juneau.parser.*; /** * OpenAPI part parser. + * + * <table class='styled'> + * <tr><th>Type</th><th>Format</th><th>Valid parameter types</th></tr> + * <tr> + * <td ><code>string</code></td> + * <td> + * <code>byte</code> + * <br><code>binary</code> + * <br><code>binary-spaced</br> + * </td> + * <td> + * <ul> + * <li><code><jk>byte</jk>[]</code> + * <li>{@link InputStream} - Returns a {@link ByteArrayInputStream}. + * <li>{@link Reader} - Returns a {@link InputStreamReader} wrapped around a {@link ByteArrayInputStream}. + * <li>{@link String} - Constructed using {@link String#String(byte[])}. + * <li>Any POJO transformable from a <code><jk>byte</jk>[]</code> (via constructors or static create methods). + * </ul> + * </td> + * </tr> + * <tr> + * <td ><code>string</code></td> + * <td> + * <code>date</code> + * <code>date-time</code> + * </td> + * <td> + * <ul> + * <li>{@link Calendar} + * <li>{@link Date} + * <li>{@link GregorianCalendar} + * <li>{@link String} - Converted using {@link Calendar#toString()}. + * <li>Any POJO transformable from a {@link Calendar} (via constructors or static create methods). + * </ul> + * </td> + * </tr> + * <tr> + * <td ><code>string</code></td> + * <td><code>uon</code></td> + * <td> + * <ul> + * <li>Any <a href='../../../../overview-summary#juneau-marshall.PojoCategories'>parsable POJO</a>. + * </ul> + * </td> + * </tr> + * <tr> + * <td ><code>string</code></td> + * <td>none specified</td> + * <td> + * <ul> + * <li>{@link String} + * <li>Any POJO transformable from a {@link String} (via constructors or static create methods). + * </ul> + * </td> + * </tr> + * </table> */ public class OpenApiPartParser extends UonPartParser { @@ -98,6 +157,23 @@ public class OpenApiPartParser extends UonPartParser { return new UonPartParserBuilder(); } + /** + * Convenience method for parsing a part. + * + * @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 If a problem occurred while trying to parse the input. + * @throws SchemaValidationParseException If the input or resulting HTTP part object fails schema validation. + */ + public <T> T parse(HttpPartSchema schema, String in, Class<T> type) throws ParseException, SchemaValidationParseException { + return parse(null, schema, in, BeanContext.DEFAULT.getClassMeta(type)); + } + @Override /* HttpPartParser */ public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) throws ParseException, SchemaValidationParseException { schema = ObjectUtils.firstNonNull(schema, this.schema, HttpPartSchema.DEFAULT); @@ -124,9 +200,11 @@ public class OpenApiPartParser extends UonPartParser { return (T)StringUtils.base64Decode(in); case DATE: case DATE_TIME: - return (T)StringUtils.parseIsoDate(in); + return (T)StringUtils.parseIsoCalendar(in); case BINARY: return (T)StringUtils.fromHex(in); + case BINARY_SPACED: + return (T)StringUtils.fromSpacedHex(in); case UON: return super.parse(partType, schema, in, type); default: @@ -138,9 +216,11 @@ public class OpenApiPartParser extends UonPartParser { return toType(StringUtils.base64Decode(in), type); case DATE: case DATE_TIME: - return toType(StringUtils.parseIsoDate(in), type); + return toType(StringUtils.parseIsoCalendar(in), type); case BINARY: return toType(StringUtils.fromHex(in), type); + case BINARY_SPACED: + return toType(StringUtils.fromSpacedHex(in), type); case UON: return super.parse(partType, schema, in, type); default: 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 9b0c980..72816ff 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 @@ -1193,6 +1193,23 @@ public final class StringUtils { public static Date parseIsoDate(String date) throws IllegalArgumentException { if (isEmpty(date)) return null; + return parseIsoCalendar(date).getTime(); + } + + /** + * Parses an ISO8601 string into a calendar. + * + * <p> + * Supports any of the following formats: + * <br><code>yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddThh, yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS</code> + * + * @param date The date string. + * @return The parsed calendar. + * @throws IllegalArgumentException + */ + public static Calendar parseIsoCalendar(String date) throws IllegalArgumentException { + if (isEmpty(date)) + return null; date = date.trim().replace(' ', 'T'); // Convert to 'standard' ISO8601 if (date.indexOf(',') != -1) // Trim milliseconds date = date.substring(0, date.indexOf(',')); @@ -1206,7 +1223,7 @@ public final class StringUtils { date += ":00:00"; else if (date.matches("\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}\\:\\d{2}")) date += ":00"; - return DatatypeConverter.parseDateTime(date).getTime(); + return DatatypeConverter.parseDateTime(date); } /**