org.eel.kitchen.jsonschema.keyword.KeywordFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.eel.kitchen.jsonschema.keyword.KeywordFactory.java

Source

/*
 * Copyright (c) 2012, Francis Galiegue <fgaliegue@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * Lesser GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.eel.kitchen.jsonschema.keyword;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.eel.kitchen.jsonschema.bundle.KeywordBundle;
import org.eel.kitchen.jsonschema.report.Domain;
import org.eel.kitchen.jsonschema.report.Message;
import org.eel.kitchen.jsonschema.report.ValidationReport;
import org.eel.kitchen.jsonschema.syntax.SyntaxValidator;
import org.eel.kitchen.jsonschema.util.JacksonUtils;
import org.eel.kitchen.jsonschema.util.NodeType;
import org.eel.kitchen.jsonschema.validator.ValidationContext;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;

/**
 * Factory to provide a set of {@link KeywordValidator} instances for a given
 * schema
 *
 * <p>This class is only called once the schemas has been deemed valid,
 * that is, after the following is true:</p>
 *
 * <ul>
 *     <li>the JSON document is not a JSON reference (ie, if it was,
 *     it has been resolved successfully);</li>
 *     <li>it is syntactically valid (see {@link SyntaxValidator}).</li>
 * </ul>
 */
public final class KeywordFactory {
    /**
     * Our existing set of keyword validators
     */
    private final Map<String, Class<? extends KeywordValidator>> validators;

    /**
     * The only constructor
     *
     * @param bundle The keyword bundle to use
     */
    public KeywordFactory(final KeywordBundle bundle) {
        validators = ImmutableMap.copyOf(bundle.getValidators());
    }

    /**
     * Return the set of validators for a particular schema
     *
     * @param schema the schema as a {@link JsonNode}
     * @return the set of validators
     */
    public Set<KeywordValidator> getValidators(final JsonNode schema) {
        final Set<KeywordValidator> ret = Sets.newHashSet();

        final Set<String> set = JacksonUtils.fieldNames(schema);

        set.retainAll(validators.keySet());

        KeywordValidator validator;

        for (final String keyword : set) {
            validator = buildValidator(validators.get(keyword), schema);
            if (!validator.alwaysTrue())
                ret.add(validator);
        }

        return ImmutableSet.copyOf(ret);
    }

    /**
     * Build one validator
     *
     * <p>This is done by reflection. Remember that the contract is to have a
     * constructor which takes a {@link JsonNode} as an argument.
     * </p>
     *
     * <p>If instantiation fails for whatever reason, an "invalid validator" is
     * returned which always fails.</p>
     *
     * @see #invalidValidator(Class, Exception)
     *
     * @param c the keyword validator class
     * @param schema the schema
     * @return the instantiated keyword validator
     */
    private static KeywordValidator buildValidator(final Class<? extends KeywordValidator> c,
            final JsonNode schema) {
        final Constructor<? extends KeywordValidator> constructor;

        try {
            constructor = c.getConstructor(JsonNode.class);
        } catch (NoSuchMethodException e) {
            return invalidValidator(c, e);
        }

        try {
            return constructor.newInstance(schema);
        } catch (InstantiationException e) {
            return invalidValidator(c, e);
        } catch (IllegalAccessException e) {
            return invalidValidator(c, e);
        } catch (InvocationTargetException e) {
            return invalidValidator(c, e);
        }
    }

    /**
     * Build an invalid validator in the event of instantiation failure
     *
     * @param e the exception raised by the instantiation attempt
     * @return a keyword validator which always fails
     */
    private static KeywordValidator invalidValidator(final Class<? extends KeywordValidator> c, final Exception e) {
        final String className = c.getName();

        return new KeywordValidator(className, NodeType.values()) {
            @Override
            protected void validate(final ValidationContext context, final ValidationReport report,
                    final JsonNode instance) {
                final Message.Builder msg = Domain.VALIDATION.newMessage().setMessage("cannot build validator")
                        .setKeyword(className).addInfo("exception", e.getClass().getName())
                        .addInfo("exceptionMessage", e.getMessage()).setFatal(true);
                report.addMessage(msg.build());
            }

            @Override
            public String toString() {
                return className;
            }
        };
    }
}