org.everit.json.schema.ArraySchema.java Source code

Java tutorial

Introduction

Here is the source code for org.everit.json.schema.ArraySchema.java

Source

/*
 * Copyright (C) 2011 Everit Kft. (http://www.everit.org)
 *
 * Licensed 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.everit.json.schema;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.json.JSONArray;

/**
 * Array schema validator.
 */
public class ArraySchema extends Schema {

    /**
     * Builder class for {@link ArraySchema}.
     */
    public static class Builder extends Schema.Builder<ArraySchema> {

        private boolean requiresArray = true;

        private Integer minItems;

        private Integer maxItems;

        private boolean uniqueItems = false;

        private Schema allItemSchema;

        private List<Schema> itemSchemas = null;

        private boolean additionalItems = true;

        private Schema schemaOfAdditionalItems;

        /**
         * Adds an item schema for tuple validation. The array items of the subject under validation
         * will be matched to expected schemas by their index. In other words the {n}th
         * {@code addItemSchema()} invocation defines the expected schema of the {n}th item of the array
         * being validated.
         *
         * @param itemSchema
         *          the schema of the next item.
         * @return this
         */
        public Builder addItemSchema(final Schema itemSchema) {
            if (itemSchemas == null) {
                itemSchemas = new ArrayList<Schema>();
            }
            itemSchemas.add(Objects.requireNonNull(itemSchema, "itemSchema cannot be null"));
            return this;
        }

        public Builder schemaOfAdditionalItems(final Schema schemaOfAdditionalItems) {
            this.schemaOfAdditionalItems = schemaOfAdditionalItems;
            return this;
        }

        public Builder requiresArray(final boolean requiresArray) {
            this.requiresArray = requiresArray;
            return this;
        }

        public Builder additionalItems(final boolean additionalItems) {
            this.additionalItems = additionalItems;
            return this;
        }

        public Builder allItemSchema(final Schema allItemSchema) {
            this.allItemSchema = allItemSchema;
            return this;
        }

        @Override
        public ArraySchema build() {
            return new ArraySchema(this);
        }

        public Builder maxItems(final Integer maxItems) {
            this.maxItems = maxItems;
            return this;
        }

        public Builder minItems(final Integer minItems) {
            this.minItems = minItems;
            return this;
        }

        public Builder uniqueItems(final boolean uniqueItems) {
            this.uniqueItems = uniqueItems;
            return this;
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    private final Integer minItems;

    private final Integer maxItems;

    private final boolean uniqueItems;

    private final Schema allItemSchema;

    private final boolean additionalItems;

    private final List<Schema> itemSchemas;

    private final boolean requiresArray;

    private final Schema schemaOfAdditionalItems;

    /**
     * Constructor.
     *
     * @param builder
     *          contains validation criteria.
     */
    public ArraySchema(final Builder builder) {
        super(builder);
        this.minItems = builder.minItems;
        this.maxItems = builder.maxItems;
        this.uniqueItems = builder.uniqueItems;
        this.allItemSchema = builder.allItemSchema;
        this.itemSchemas = builder.itemSchemas;
        if (!builder.additionalItems && allItemSchema != null) {
            additionalItems = true;
        } else {
            additionalItems = builder.schemaOfAdditionalItems != null || builder.additionalItems;
        }
        this.schemaOfAdditionalItems = builder.schemaOfAdditionalItems;
        if (!(allItemSchema == null || itemSchemas == null)) {
            throw new SchemaException("cannot perform both tuple and list validation");
        }
        this.requiresArray = builder.requiresArray;
    }

    public Schema getAllItemSchema() {
        return allItemSchema;
    }

    public List<Schema> getItemSchemas() {
        return itemSchemas;
    }

    public Integer getMaxItems() {
        return maxItems;
    }

    public Integer getMinItems() {
        return minItems;
    }

    public boolean needsUniqueItems() {
        return uniqueItems;
    }

    public boolean permitsAdditionalItems() {
        return additionalItems;
    }

    private void testItemCount(final JSONArray subject) {
        int actualLength = subject.length();
        if (minItems != null && actualLength < minItems) {
            throw new ValidationException("expected minimum item count: " + minItems + ", found: " + actualLength);
        }
        if (maxItems != null && maxItems < actualLength) {
            throw new ValidationException("expected maximum item count: " + minItems + ", found: " + actualLength);
        }
    }

    private void testItems(final JSONArray subject) {
        if (allItemSchema != null) {
            for (int i = 0; i < subject.length(); ++i) {
                allItemSchema.validate(subject.get(i));
            }
        } else if (itemSchemas != null) {
            if (!additionalItems && subject.length() > itemSchemas.size()) {
                throw new ValidationException(String.format("expected: [%d] array items, found: [%d]",
                        itemSchemas.size(), subject.length()));
            }
            int itemValidationUntil = Math.min(subject.length(), itemSchemas.size());
            for (int i = 0; i < itemValidationUntil; ++i) {
                itemSchemas.get(i).validate(subject.get(i));
            }
            if (schemaOfAdditionalItems != null) {
                for (int i = itemValidationUntil; i < subject.length(); ++i) {
                    schemaOfAdditionalItems.validate(subject.get(i));
                }
            }
        }
    }

    private void testUniqueness(final JSONArray subject) {
        if (subject.length() == 0) {
            return;
        }
        Collection<Object> uniqueItems = new ArrayList<Object>(subject.length());
        for (int i = 0; i < subject.length(); ++i) {
            Object item = subject.get(i);
            for (Object contained : uniqueItems) {
                if (ObjectComparator.deepEquals(contained, item)) {
                    throw new ValidationException("array items are not unique");
                }
            }
            uniqueItems.add(item);
        }
    }

    @Override
    public void validate(final Object subject) {
        if (!(subject instanceof JSONArray)) {
            if (requiresArray) {
                throw new ValidationException(JSONArray.class, subject);
            }
        } else {
            JSONArray arrSubject = (JSONArray) subject;
            testItemCount(arrSubject);
            if (uniqueItems) {
                testUniqueness(arrSubject);
            }
            testItems(arrSubject);
        }
    }

    public boolean requiresArray() {
        return requiresArray;
    }

    public Schema getSchemaOfAdditionalItems() {
        return schemaOfAdditionalItems;
    }

}