ConfigValidator.java

/*
 * Copyright 2013 University of Glasgow.
 *
 * 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 broadwick.config;

import broadwick.config.generated.Models;
import broadwick.config.generated.Project;
import broadwick.model.Model;
import lombok.extern.slf4j.Slf4j;

/**
 * Validator class for configuration files supplied to Broadwick, the framework for epidemiological modelling.
 */
@Slf4j
public final class ConfigValidator {

    /**
     * Create an validator instance that is capable of checking the content of a configuration file.
     * @param project the contents of the [read] configuration file. project.
     */
    public ConfigValidator(final Project project) {
        this.project = project;
    }

    /**
     * Validate the project.
     * @return a ConfigValidationErrors object that contains all the errors (if any).
     */
    public ConfigValidationErrors validate() {
        try {
            validateModels();
        } catch (Exception e) {
            errors.addError("Could not validate the configuration file. " + e.getLocalizedMessage());
        }

        return errors;
    }

    /**
     * Validate the Models section of the configuration file. Any errors will be saved in the local ValidationErrors.
     */
    private void validateModels() {
        for (Models.Model model : project.getModels().getModel()) {

            // we create (using reflection) the model(s) in the configuration file and check that the parameters
            // and priors in the configuration file are valid for the model (by checking that the model has fields
            // marked with the @Parameter and @Prior annotations).
            final String clazz = model.getClassname();
            final Model modelObj = this.<Model>createObject(Model.class, clazz);

            if (modelObj == null) {
                errors.addError(String.format("Could not create class [%s]. Does it exist?", clazz));
            }
            // TODO else perform any validation
        }
    }

    /**
     * If the project has been validated, i.e. it is structurally ok, then this method will simply return the project
     * supplied in this objects constructor, else it will return null.
     * @return null if the project is invalid (i.e. contains major errors) else returns the project object supplied to
     *         the constructor.
     */
    public Project getValidatedProject() {
        if (errors.isValid()) {
            return project;
        } else {
            return null;
        }
    }

    /**
     * Create an object through reflection, to use this method to create a solver object
     * <code>
     * Solver solverObj = this.<Solver>createObject(Solver.class, "RungeKutta4");
     * </code>
     * @param <T>   the type of object to create.
     * @param clazz the class type of the object we will create.
     * @param name  the name of the class that is to be instantiated.
     * @return the created object.
     */
    private <T> T createObject(final Class<T> clazz, final String name) {
        T object = null;
        try {
            // Check if the class exists
            object = clazz.cast(Class.forName(name).newInstance());

        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
            errors.addError("Could not find " + clazz.getCanonicalName() + " class <" + name + ">");
        }
        return object;
    }

    private Project project;
    private ConfigValidationErrors errors = new ConfigValidationErrors();
}