187 lines
7.4 KiB
C++
187 lines
7.4 KiB
C++
#include "comdelvalidator.h"
|
|
#include "library.h"
|
|
|
|
namespace domain {
|
|
|
|
std::vector<ValidationError> ComdelValidator::validateSchema(Schema &schema, ValidationContext context) {
|
|
std::vector<ValidationError> 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<ValidationError> ComdelValidator::validateInstanceCount(Schema& schema, Library& library, ValidationContext context) {
|
|
std::vector<ValidationError> errors;
|
|
|
|
// validate instance count
|
|
std::map<std::string, int> 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<std::string, int> 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<ValidationError> ComdelValidator::validatePinConnections(Schema &schema, Library &library, ValidationContext context) {
|
|
std::vector<ValidationError> 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<ValidationError> ComdelValidator::validateComponent(ComponentInstance* instance, ValidationContext context) {
|
|
std::vector<ValidationError> 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<ValidationError> ComdelValidator::validateAttribute(InstanceAttribute *attribute, ValidationContext context) {
|
|
std::vector<ValidationError> errors;
|
|
if(attribute->attribute.getPopup()) {
|
|
Popup popup = *attribute->attribute.getPopup();
|
|
context.attribute = attribute;
|
|
context.attributes = std::map<std::string, Value>{{attribute->name, attribute->value}};
|
|
|
|
for(auto &rule: popup.getRules()) {
|
|
auto result = validateRule(rule, context);
|
|
if(result) {
|
|
errors.push_back(*result);
|
|
}
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
std::optional<ValidationError> 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<ComponentInstance> &component, Pin &pin) {
|
|
for(auto conn: schema.connections) {
|
|
auto busConnection = dynamic_cast<BusConnectionInstance*>(conn.get());
|
|
if(busConnection != nullptr) {
|
|
if(busConnection->instance->name == component->name && busConnection->connection.getComponent().pin == pin.getName()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
auto directConnection = dynamic_cast<DirectConnectionInstance*>(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;
|
|
}
|
|
|
|
} |