package ru.semiot.services.tsdbservice.model;
import static ru.semiot.services.tsdbservice.ServiceConfig.CONFIG;
import com.datastax.driver.core.UDTValue;
import org.apache.jena.datatypes.TypeMapper;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.vocabulary.RDF;
import ru.semiot.commons.namespaces.DUL;
import ru.semiot.commons.namespaces.SEMIOT;
import ru.semiot.services.tsdbservice.TSDBClient;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
public class CommandResult {
private static final String SYSTEMS_URI_PREFIX = CONFIG.rootUrl() + "systems/";
private static final String PROCESSES_PATH_URI = "processes";
private final String systemId;
private final String processId;
private final ZonedDateTime eventTime;
private final String type;
private final List<CommandProperty> properties = new ArrayList<>();
private final Map<Resource, RDFNode> parameters = new HashMap<>();
public CommandResult(@NotNull String systemId, @NotNull String processId,
@NotNull String eventTime, @NotNull String type) {
this.systemId = systemId;
this.processId = processId;
this.eventTime = ZonedDateTime.parse(eventTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
this.type = type;
}
public void addProperty(Property property, RDFNode value) {
if (value.isLiteral() || value.isURIResource()) {
properties.add(new CommandProperty(property, value));
}
}
public void addProperties(List<UDTValue> properties) {
if (properties != null && !properties.isEmpty()) {
for (UDTValue value : properties) {
Property property = ResourceFactory.createProperty(value.getString("property"));
RDFNode propertyValue;
if (value.isNull("datatype")) {
propertyValue = ResourceFactory.createResource(value.getString("value"));
} else {
propertyValue = ResourceFactory.createTypedLiteral(value.getString("value"),
TypeMapper.getInstance().getSafeTypeByName(value.getString("datatype")));
}
addProperty(property, propertyValue);
}
}
}
public void addParameter(Resource parameter, RDFNode value) {
parameters.put(parameter, value);
}
public void addParameters(List<UDTValue> parameters) {
if (parameters != null && !parameters.isEmpty()) {
for (UDTValue value : parameters) {
Resource parameter = ResourceFactory.createResource(value.getString("for_parameter"));
RDFNode parameterValue = ResourceFactory.createTypedLiteral(value.getString("value"),
TypeMapper.getInstance().getSafeTypeByName(value.getString("datatype")));
addParameter(parameter, parameterValue);
}
}
}
public String toInsertQuery() {
StringBuilder builderProps = new StringBuilder("[");
for (CommandProperty p : properties) {
builderProps.append(p.toCQLValue()).append(",");
}
builderProps.deleteCharAt(builderProps.length() - 1);
builderProps.append("]");
StringBuilder builderParams = new StringBuilder("[");
for (Resource parameter : parameters.keySet()) {
builderParams.append("{").append("for_parameter:'").append(parameter.getURI())
.append("',value:'").append(parameters.get(parameter).asLiteral().getLexicalForm())
.append("',datatype:'").append(parameters.get(parameter).asLiteral().getDatatypeURI())
.append("'},");
}
builderParams.deleteCharAt(builderParams.length() - 1);
builderParams.append("]");
String commandProperties = properties.isEmpty() ? null : builderProps.toString();
String commandParameters = parameters.isEmpty() ? null : builderParams.toString();
return String.format(
"INSERT INTO semiot.commandresult " +
"(system_id, process_id, event_time, " +
"command_properties, command_parameters, command_type)" +
"VALUES ('%s', '%s', '%s', %s, %s, '%s')",
systemId, processId, TSDBClient.formatToCQLTimestamp(eventTime),
commandProperties, commandParameters, type);
}
public Model toRDF() {
Model model = ModelFactory.createDefaultModel();
Resource commandResult = ResourceFactory.createResource();
Resource command = ResourceFactory.createResource();
Resource system = ResourceFactory.createResource(SYSTEMS_URI_PREFIX + systemId);
Resource process = ResourceFactory.createResource(system.getURI()
+ "/" + PROCESSES_PATH_URI + "/" + processId);
model.add(commandResult, RDF.type, SEMIOT.CommandResult)
.add(commandResult, SEMIOT.isResultOf, command)
.add(commandResult, DUL.hasEventTime,
eventTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
.add(commandResult, DUL.hasParticipant, system);
model.add(command, RDF.type, SEMIOT.Command)
.add(command, RDF.type, ResourceFactory.createResource(type))
.add(command, DUL.associatedWith, system)
.add(command, SEMIOT.forProcess, process);
for (Resource parameter : parameters.keySet()) {
Resource parameterResource = ResourceFactory.createResource();
model.add(command, DUL.hasParameter, parameterResource)
.add(parameterResource, SEMIOT.forParameter, parameter)
.add(parameterResource, DUL.hasParameterDataValue, parameters.get(parameter));
}
for (CommandProperty property : properties) {
model.add(command, property.property, property.value);
}
return model;
}
private class CommandProperty {
private final Property property;
private final RDFNode value;
CommandProperty(Property property, RDFNode value) {
this.property = property;
this.value = value;
}
public String toCQLValue() {
if (value.isLiteral()) {
Literal l = (Literal) value;
return String.format("{property:'%s',value:'%s',datatype:'%s'}",
property.getURI(), l.getLexicalForm(), l.getDatatypeURI());
} else {
Resource r = (Resource) value;
return String.format("{property:'%s',value:'%s',datatype:null}",
property.getURI(), r.getURI());
}
}
}
}