#include "comdel_validator.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; } ComdelValidator::ComdelValidator(std::vector validators) { for (auto *validator: validators) { validator->clear(); this->validators.insert(std::make_pair(validator->getName(), validator)); } } }