diff --git a/src/main/java/es/elixir/bsc/json/schema/model/JsonArraySchema.java b/src/main/java/es/elixir/bsc/json/schema/model/JsonArraySchema.java index e86e841..c0b1852 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/JsonArraySchema.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/JsonArraySchema.java @@ -36,6 +36,7 @@ public interface JsonArraySchema extends JsonSchema { public final static String ITEMS = "items"; + public final static String PREFIX_ITEMS = "prefixItems"; public final static String UNIQUE_ITEMS = "uniqueItems"; public final static String ADDITIONAL_ITEMS = "additionalItems"; public final static String UNEVALUATED_ITEMS = "unevaluatedItems"; @@ -54,19 +55,33 @@ public interface JsonArraySchema extends JsonSchema { void setMaxItems(Long maxItems); /** + * returns a list that contain one or more schemas. + * + * In 2020-12 returns immutable list with just one schema. + * In 2019-09 and before the list may contain more schemas. + * * In a case where there is only one schema in the list it is - * either {...} or [{...}].Setting 'additionalItems' to NULL - * means there is a single schema and not an array with only one schema defined. - * - * @param + * either {...} or [{...}]. + * + * @param any implementation specific class that implements JsonSchema * * @return list of schemas */ List getItems(); + /** + * Returns 2020-12 'prefixItems' + * + * @param any implementation specific class that implements JsonSchema + * @return + */ + List getPrefixItems(); + Boolean isUniqueItems(); /** + * Returns 'additionalSchema' or 'items' in the 2020-09 + * * @param * * @return 'additionalSchema' JsonSchema, NULL if FALSE (or not set), diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java index fb3460e..c5351ff 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java @@ -56,7 +56,11 @@ public class JsonArraySchemaImpl extends PrimitiveSchemaImpl implements JsonArraySchema { - private List items; + private final List items; + + // the items are actually a 'prefixItems' + private boolean prefixItems; + private Boolean additionalItems; private Boolean uniqueItems; private AbstractJsonSchema additionalItemsSchema; @@ -74,6 +78,8 @@ public class JsonArraySchemaImpl extends PrimitiveSchemaImpl public JsonArraySchemaImpl(AbstractJsonSchemaElement parent, JsonSchemaLocator locator, String jsonPointer) { super(parent, locator, jsonPointer); + + items = new ArrayList(); } @Override @@ -93,11 +99,15 @@ public Stream getChildren() { @Override public List getItems() { - if (items == null) { - items = new ArrayList<>(); - } - - return items; + // in 2020-12 'items' is a schema + return prefixItems ? additionalItemsSchema != null ? List.of(additionalItemsSchema) : null : items; + } + + @Override + public List getPrefixItems() { + // 2019-09 'items' == 2020-12 'prefixItems' + // 2019-09 'additionalItemsSchema' == 2020-12 'items' + return prefixItems ? items : null; } @Override @@ -155,7 +165,7 @@ public JsonArraySchemaImpl read(JsonSubschemaParser parser, JsonObject object) throws JsonSchemaException { super.read(parser, object); - + final JsonNumber min = JsonSchemaUtil.check(object.getJsonNumber(MIN_ITEMS), JsonValue.ValueType.NUMBER); if (min != null) { minItems = min.longValue(); @@ -192,47 +202,59 @@ public JsonArraySchemaImpl read(JsonSubschemaParser parser, JsonObject object) } JsonValue jitems = object.get(ITEMS); - if (jitems != null) { + JsonValue jadditionalItems = object.get(ADDITIONAL_ITEMS); + + JsonArray jprefixitems = JsonSchemaUtil.check(object.get(PREFIX_ITEMS), JsonValue.ValueType.ARRAY); + if (jprefixitems != null) { + // in 2020-12 when 'prefixItems' is defined 'items' are 'additionalItems' + prefixItems = true; + jadditionalItems = jitems; + } else if (jitems != null) { switch(jitems.getValueType()) { case OBJECT: case TRUE: case FALSE: final AbstractJsonSchema schema = parser.parse(locator, this, getJsonPointer() + "/" + ITEMS, jitems, null); - getItems().add(schema); + items.add(schema); break; - case ARRAY: additionalItems = true; - for (int i = 0, n = jitems.asJsonArray().size(); i < n; i++) { - final JsonValue value = jitems.asJsonArray().get(i); - switch(value.getValueType()) { - case OBJECT: - case TRUE: - case FALSE: final AbstractJsonSchema arr = parser.parse(locator, this, getJsonPointer() + "/" + ITEMS + "/" + i, value, null); - getItems().add(arr); - break; - default: throw new JsonSchemaException(new ParsingError(ParsingMessage.INVALID_ATTRIBUTE_TYPE, - ITEMS + "/" + i, value.getValueType().name(), "either an object or boolean")); - } - } + case ARRAY: jprefixitems = jitems.asJsonArray(); break; default: throw new JsonSchemaException(new ParsingError(ParsingMessage.INVALID_ATTRIBUTE_TYPE, ITEMS, jitems.getValueType().name(), "either an object, boolean or an array")); - } } - - if (additionalItems != null) { - final JsonValue jadditionalItems = object.get(ADDITIONAL_ITEMS); - if (jadditionalItems != null) { - switch(jadditionalItems.getValueType()) { - case OBJECT: additionalItems = null; break; - case FALSE: additionalItems = false; - case TRUE: break; - default: throw new JsonSchemaException(new ParsingError(ParsingMessage.INVALID_ATTRIBUTE_TYPE, - ADDITIONAL_ITEMS, jadditionalItems.getValueType().name(), "either object or boolean")); + + if (jprefixitems != null) { + additionalItems = true; + + final String propertyName = prefixItems ? PREFIX_ITEMS : ITEMS; + + for (int i = 0, n = jprefixitems.size(); i < n; i++) { + final JsonValue value = jprefixitems.get(i); + switch(value.getValueType()) { + case OBJECT: + case TRUE: + case FALSE: final AbstractJsonSchema arr = parser.parse(locator, this, getJsonPointer() + "/" + propertyName + "/" + i, value, null); + items.add(arr); + break; + default: throw new JsonSchemaException(new ParsingError(ParsingMessage.INVALID_ATTRIBUTE_TYPE, + propertyName + "/" + i, value.getValueType().name(), "either an object or boolean")); } - additionalItemsSchema = parser.parse(locator, this, getJsonPointer() + "/" + ADDITIONAL_ITEMS, jadditionalItems, null); } } + if (additionalItems != null && jadditionalItems != null) { + final String propertyName = jadditionalItems == jitems ? ITEMS : ADDITIONAL_ITEMS; + + switch(jadditionalItems.getValueType()) { + case OBJECT: break; + case FALSE: additionalItems = false; + case TRUE: break; + default: throw new JsonSchemaException(new ParsingError(ParsingMessage.INVALID_ATTRIBUTE_TYPE, + propertyName, jadditionalItems.getValueType().name(), "either object or boolean")); + } + additionalItemsSchema = parser.parse(locator, this, getJsonPointer() + "/" + propertyName, jadditionalItems, null); + } + final JsonValue junevaluatedItems = object.get(UNEVALUATED_ITEMS); if (junevaluatedItems != null) { switch(junevaluatedItems.getValueType()) { diff --git a/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaItemsTest.java b/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaItemsTest.java index 0b478b3..08d4063 100644 --- a/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaItemsTest.java +++ b/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaItemsTest.java @@ -59,8 +59,8 @@ public void test_draft201909() { test(JSON_DRAFT201909_TEST_FILE); } -// @Test -// public void test_draft202012() { -// test(JSON_DRAFT202012_TEST_FILE); -// } + @Test + public void test_draft202012() { + test(JSON_DRAFT202012_TEST_FILE); + } } diff --git a/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaPrefixItemsTest.java b/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaPrefixItemsTest.java new file mode 100644 index 0000000..94d7a4b --- /dev/null +++ b/src/test/java/es/elixir/bsc/json/schema/org/tests/JsonSchemaPrefixItemsTest.java @@ -0,0 +1,42 @@ +/** + * ***************************************************************************** + * Copyright (C) 2024 ELIXIR ES, Spanish National Bioinformatics Institute (INB) + * and Barcelona Supercomputing Center (BSC) + * + * Modifications to the initial code base are copyright of their respective + * authors, or their employers as appropriate. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + ***************************************************************************** + */ + +package es.elixir.bsc.json.schema.org.tests; + +import org.junit.Test; + +/** + * @author Dmitry Repchevsky + */ + +public class JsonSchemaPrefixItemsTest extends JsonSchemaOrgTest { + + private final static String JSON_DRAFT202012_TEST_FILE = "json-schema-org/tests/draft2020-12/prefixItems.json"; + + @Test + public void test_draft202012() { + test(JSON_DRAFT202012_TEST_FILE); + } +}