#include "comdelvalidator.h" #include "library.h" namespace domain { std::vector ComdelValidator::validateSchema(Schema &schema, ValidationContext context) { std::vector errors; context.instance = nullptr; context.attribute = nullptr; for(auto &instance: schema.componentInstances) { auto result = validateComponent(instance.get(), context); errors.insert(errors.end(), result.begin(), result.end()); } return errors; } std::vector ComdelValidator::validateInstanceCount(Schema& schema, Library& library, ValidationContext context) { std::vector errors; // validate instance count std::map instanceMap; for(auto& inst: schema.componentInstances) { instanceMap[inst->component.getName()]++; } for(auto comp: library.getComponents()) { int count = instanceMap[comp.getName()]; context.attributes["componentName"] = Value::fromString(comp.getName()); context.attributes["min"] = Value::fromInt(comp.getCount().first); context.attributes["max"] = Value::fromInt(comp.getCount().second); context.attributes["count"] = Value::fromInt(count); if(count < comp.getCount().first) { auto message = populateMessage("Not enough instances of component '{componentName}' required at least {min}, found {count}", context); errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); } else if(count > comp.getCount().second) { auto message = populateMessage("To many instances of component '{componentName}' allow at most {max}, found {count}", context); errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); } } // validate bus instance count std::map busInstanceMap; for(auto& inst: schema.busInstances) { busInstanceMap[inst->bus.getName()]++; } for(auto bus: library.getBuses()) { int count = busInstanceMap[bus.getName()]; context.attributes["busName"] = Value::fromString(bus.getName()); context.attributes["min"] = Value::fromInt(bus.getCount().first); context.attributes["max"] = Value::fromInt(bus.getCount().second); context.attributes["count"] = Value::fromInt(count); if(count < bus.getCount().first) { auto message = populateMessage("Not enough instances of bus '{busName}' required at least {min}, found {count}", context); errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); } else if(count > bus.getCount().second) { auto message = populateMessage("To many instances of bus '{busName}' allow at most {max}, found {count}", context); errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); } } return errors; } std::vector ComdelValidator::validatePinConnections(Schema &schema, Library &library, ValidationContext context) { std::vector errors; for(auto& inst: schema.componentInstances) { for(auto& pin: inst->component.getPins()) { if(pin.getConnection().getType() == PinConnection::REQUIRED) { if(!connectionExists(schema, inst, pin)) { context.instance = inst.get(); context.attributes["instanceName"] = Value::fromString(inst->name); auto message = populateMessage(pin.getConnection().getMessage(), context); errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message}); } } } } return errors; } std::vector ComdelValidator::validateComponent(ComponentInstance* instance, ValidationContext context) { std::vector errors; context.instance = instance; context.attributes.clear(); for(auto &attribute: instance->attributes) { context.attributes[attribute.name] = attribute.value; } for(auto &rule: instance->component.getRules()) { auto result = validateRule(rule, context); if(result) { errors.push_back(*result); } } for(auto &attribute: instance->attributes) { auto result = validateAttribute(&attribute, context); errors.insert(errors.end(), result.begin(), result.end()); } return errors; } std::vector ComdelValidator::validateAttribute(InstanceAttribute *attribute, ValidationContext context) { std::vector errors; if(attribute->attribute.getPopup()) { Popup popup = *attribute->attribute.getPopup(); context.attribute = attribute; context.attributes = std::map{{attribute->name, attribute->value}}; for(auto &rule: popup.getRules()) { auto result = validateRule(rule, context); if(result) { errors.push_back(*result); } } } return errors; } std::optional ComdelValidator::validateRule(Rule rule, ValidationContext context) { RuleContext ruleContext; ruleContext.addressSpaces = context.addressSpaces; ruleContext.attributes = context.attributes; ruleContext.function = validators; auto action = rule.evaluate(ruleContext); if (action) { std::string message = this->populateMessage(action->getMessage(), context); return ValidationError{context.instance, context.attribute, action->getType(), message}; } return nullopt; } std::string ComdelValidator::populateMessage(string source, ValidationContext context) { for(auto &[key, value]: context.attributes) { source = replacePlaceholder(source, key, value); } return source; } string ComdelValidator::replacePlaceholder(string source, string key, Value value) { key = "{" + key + "}"; auto placeholderValue = value.string(); auto found = source.find(key); while(found != string::npos) { source.replace(found, key.length(), placeholderValue); found = source.find(key); } return source; } bool ComdelValidator::connectionExists(Schema &schema, shared_ptr &component, Pin &pin) { for(auto conn: schema.connections) { auto busConnection = dynamic_cast(conn.get()); if(busConnection != nullptr) { if(busConnection->instance->name == component->name && busConnection->connection.getComponent().pin == pin.getName()) { return true; } } auto directConnection = dynamic_cast(conn.get()); if(directConnection != nullptr) { if(directConnection->instance->name == component->name && busConnection->connection.getComponent().pin == pin.getName()) { return true; } if(directConnection->secondInstance->name == component->name && busConnection->connection.getSecondComponent()->pin == pin.getName()) { return true; } } } return false; } }