All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.lightblue.crud.ConstraintValidator Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 Copyright 2013 Red Hat, Inc. and/or its affiliates.

 This file is part of lightblue.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the 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
 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 .
 */
package com.redhat.lightblue.crud;

import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;

import com.redhat.lightblue.util.JsonDoc;
import com.redhat.lightblue.util.Registry;
import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.Error;
import com.redhat.lightblue.util.KeyValueCursor;

import com.redhat.lightblue.metadata.EntityMetadata;
import com.redhat.lightblue.metadata.FieldTreeNode;
import com.redhat.lightblue.metadata.FieldConstraint;
import com.redhat.lightblue.metadata.EntityConstraint;
import com.redhat.lightblue.metadata.Field;
import com.redhat.lightblue.metadata.FieldCursor;
import com.redhat.lightblue.metadata.SimpleArrayElement;
import com.redhat.lightblue.metadata.ResolvedReferenceField;

public class ConstraintValidator {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConstraintValidator.class);

    private final Registry fRegistry;
    private final Registry eRegistry;
    private final EntityMetadata md;

    private final Map> docErrors = new HashMap<>();
    private final List errors = new ArrayList<>();

    private List currentDocList;
    private JsonDoc currentDoc;
    private FieldTreeNode currentFieldNode;
    private Path currentFieldPath;
    private FieldConstraint currentFieldConstraint;
    private EntityConstraint currentEntityConstraint;

    protected ConstraintValidator(Registry r,
                                  Registry e,
                                  EntityMetadata md) {
        this.fRegistry = r;
        this.eRegistry = e;
        this.md = md;
    }

    public void clearErrors() {
        docErrors.clear();
        errors.clear();
    }

    public List getCurrentDocList() {
        return currentDocList;
    }

    public JsonDoc getCurrentDoc() {
        return currentDoc;
    }

    public FieldTreeNode getCurrentFieldMetadata() {
        return currentFieldNode;
    }

    public Path getCurrentFieldPath() {
        return currentFieldPath;
    }

    public FieldConstraint getCurrentFieldConstraint() {
        return currentFieldConstraint;
    }

    public EntityConstraint getCurrentEntityConstraint() {
        return currentEntityConstraint;
    }

    public Map> getDocErrors() {
        return docErrors;
    }

    public void addDocError(Error err) {
        getDocError().add(err);
    }

    public void addDocErrors(List list) {
        getDocError().addAll(list);
    }

    private List getDocError() {
        if (currentDoc == null) {
            throw new IllegalStateException();
        }
        List list = docErrors.get(currentDoc);
        if (list == null) {
            list = new ArrayList<>();
            docErrors.put(currentDoc, list);
        }
        return list;
    }

    public void addError(Error err) {
        errors.add(err);
    }

    public List getErrors() {
        return errors;
    }

    public boolean hasErrors() {
        return !errors.isEmpty()
                || !docErrors.isEmpty();
    }

    public void validateDocs(List docList) {
        currentDocList = docList;
        currentDoc = null;

        LOGGER.debug("validateDocs() enter with {} docs", docList.size());
        Error.push("validateDocs");
        try {
            for (JsonDoc doc : docList) {
                validateDoc(doc);
            }
        } catch (Error e) {
            // rethrow lightblue error
            throw e;
        } catch (Exception e) {
            // throw new Error (preserves current error context)
            LOGGER.error(e.getMessage(), e);
            throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
        } finally {
            Error.pop();
        }
        LOGGER.debug("validateDocs() complete");
    }

    public void validateDoc(JsonDoc doc) {
        currentDoc = doc;
        Error.push("validateDoc");
        LOGGER.debug("validateDoc() enter with entity {}", md.getName());
        try {
            currentFieldConstraint = null;
            currentFieldNode = null;
            currentFieldPath = null;
            Path currentValuePath = null;
            JsonNode currentValue = null;
            checkEntityConstraints(doc);
            currentEntityConstraint = null;
            checkConstraints(doc, currentValuePath, currentValue);
        } catch (Error e) {
            // rethrow lightblue error
            throw e;
        } catch (Exception e) {
            // throw new Error (preserves current error context)
            LOGGER.error(e.getMessage(), e);
            throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
        } finally {
            Error.pop();
        }
        currentDoc = null;
        currentFieldConstraint = null;
        currentFieldNode = null;
        currentFieldPath = null;
        LOGGER.debug("validateDoc() complete");
    }

    private void checkEntityConstraints(JsonDoc doc) {
        LOGGER.debug("checking entity constraints");
        for (EntityConstraint x : md.getConstraints()) {
            currentEntityConstraint = x;
            String constraintType = currentEntityConstraint.getType();
            LOGGER.debug("checking entity constraint " + constraintType);
            Error.push(constraintType);
            try {
                EntityConstraintChecker checker = eRegistry.find(constraintType);
                if (checker == null) {
                    throw Error.get(CrudConstants.ERR_NO_CONSTRAINT);
                }
                checker.checkConstraint(this, currentEntityConstraint, doc);
            } catch (Error e) {
                // rethrow lightblue error
                throw e;
            } catch (Exception e) {
                // throw new Error (preserves current error context)
                LOGGER.error(e.getMessage(), e);
                throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
            } finally {
                Error.pop();
            }
        }
    }

    private void checkConstraints(JsonDoc doc, Path currentValuePath, JsonNode currentValue) {
        LOGGER.debug("checking field constraints");
        FieldCursor cursor = md.getFieldCursor();
        Path skip=null;
        while (cursor.next()) {
            currentFieldNode = cursor.getCurrentNode();
            currentFieldPath = cursor.getCurrentPath();
            // Skip any fields reached by crossing entity boundaries
            if(skip!=null) {
                if(!currentFieldPath.prefix(skip.numSegments()).equals(skip))
                    skip=null;
            }
            if(skip==null) {
                if(currentFieldNode instanceof ResolvedReferenceField)
                    skip=currentFieldNode.getFullPath();
            } 
            if(skip==null) {
                LOGGER.debug("checking field {} for {} - {}", currentFieldPath,currentFieldNode);
                Error.push(currentFieldPath.toString());
                try {
                    List constraints = null;
                    if (currentFieldNode instanceof Field) {
                        constraints = ((Field) currentFieldNode).getConstraints();
                    } else if (currentFieldNode instanceof SimpleArrayElement) {
                        constraints = ((SimpleArrayElement) currentFieldNode).getConstraints();
                    }
                    LOGGER.debug("Constraints:{}",constraints);
                    if (constraints != null && !constraints.isEmpty()) {
                        checkFieldConstraints(doc, constraints, currentValuePath, currentValue);
                    }
                } catch (Error e) {
                    // rethrow lightblue error
                    throw e;
                } catch (Exception e) {
                    // throw new Error (preserves current error context)
                    LOGGER.error(e.getMessage(), e);
                    throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
                } finally {
                    Error.pop();
                }
            }
        }
    }

    private void checkFieldConstraints(JsonDoc doc, List constraints, Path currentValuePath, JsonNode currentValue) {

        for (FieldConstraint x : constraints) {
            currentFieldConstraint = x;
            String constraintType = currentFieldConstraint.getType();
            LOGGER.debug("checking constraint " + constraintType);
            Error.push(constraintType);
            try {
                FieldConstraintChecker checker = fRegistry.find(constraintType);
                if (checker == null) {
                    throw Error.get(CrudConstants.ERR_NO_CONSTRAINT);
                }
                if (checker instanceof FieldConstraintDocChecker) {
                    // Constraint needs to be checked once for the doc
                    checkFieldContraints(doc, (FieldConstraintDocChecker) checker);
                } else if (checker instanceof FieldConstraintValueChecker) {
                    // Constraint needs to be checked for all the values in the doc
                    checkValueContraints(doc, (FieldConstraintChecker) checker, currentValuePath, currentValue);
                }
            } catch (Error e) {
                // rethrow lightblue error
                throw e;
            } catch (Exception e) {
                // throw new Error (preserves current error context)
                LOGGER.error(e.getMessage(), e);
                throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
            } finally {
                Error.pop();
            }
        }
    }

    private void checkFieldContraints(JsonDoc doc, FieldConstraintDocChecker checker) {
        ((FieldConstraintDocChecker) checker).checkConstraint(this,
                currentFieldNode,
                currentFieldPath,
                currentFieldConstraint,
                doc);
    }

    private void checkValueContraints(JsonDoc doc, FieldConstraintChecker checker, Path currentValuePath, JsonNode currentValue) {
        KeyValueCursor fieldValues = doc.getAllNodes(currentFieldPath);
        while (fieldValues.hasNext()) {
            fieldValues.next();
            currentValuePath = fieldValues.getCurrentKey();
            currentValue = fieldValues.getCurrentValue();
            Error.push(currentValuePath.toString());
            try {
                ((FieldConstraintValueChecker) checker).checkConstraint(this,
                        currentFieldNode,
                        currentFieldPath,
                        currentFieldConstraint,
                        currentValuePath,
                        doc,
                        currentValue);
            } catch (Error e) {
                // rethrow lightblue error
                throw e;
            } catch (Exception e) {
                // throw new Error (preserves current error context)
                LOGGER.error(e.getMessage(), e);
                throw Error.get(CrudConstants.ERR_CRUD, e.getMessage());
            } finally {
                Error.pop();
            }
        }
    }

    public EntityMetadata getEntityMetadata() {
        return md;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy