package won.protocol.util;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.rdf.model.impl.PropertyImpl;
import org.apache.jena.rdf.model.impl.ResourceImpl;
import org.apache.jena.riot.Lang;
import org.apache.jena.sparql.path.Path;
import org.apache.jena.sparql.path.PathParser;
import org.apache.jena.vocabulary.RDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import won.protocol.exception.IncorrectPropertyCountException;
import won.protocol.message.WonMessage;
import won.protocol.message.WonSignatureData;
import won.protocol.model.ConnectionState;
import won.protocol.model.Match;
import won.protocol.service.WonNodeInfo;
import won.protocol.service.WonNodeInfoBuilder;
import won.protocol.vocabulary.SFSIG;
import won.protocol.vocabulary.WON;
import won.protocol.vocabulary.WONMSG;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import static won.protocol.util.RdfUtils.findOrCreateBaseResource;
/**
* Utilities for populating/manipulating the RDF models used throughout the WON application.
*/
public class WonRdfUtils
{
public static final String NAMED_GRAPH_SUFFIX = "#data";
private static final Logger logger = LoggerFactory.getLogger(WonRdfUtils.class);
public static class SignatureUtils {
public static boolean isSignatureGraph(String graphUri, Model model) {
// TODO check the presence of all the required triples
Resource resource = model.getResource(graphUri);
StmtIterator si = model.listStatements(resource, RDF.type, SFSIG.SIGNATURE);
if (si.hasNext()) {
return true;
}
return false;
}
public static boolean isSignature(Model model, String modelName) {
// TODO check the presence of all the required triples
return model.contains(model.getResource(modelName), RDF.type, SFSIG.SIGNATURE);
}
public static String getSignedGraphUri(String signatureGraphUri, Model signatureGraph) {
String signedGraphUri = null;
Resource resource = signatureGraph.getResource(signatureGraphUri);
NodeIterator ni = signatureGraph.listObjectsOfProperty(resource, WONMSG.HAS_SIGNED_GRAPH_PROPERTY);
if (ni.hasNext()) {
signedGraphUri = ni.next().asResource().getURI();
}
return signedGraphUri;
}
public static String getSignatureValue(String signatureGraphUri, Model signatureGraph) {
String signatureValue = null;
Resource resource = signatureGraph.getResource(signatureGraphUri);
NodeIterator ni2 = signatureGraph.listObjectsOfProperty(resource, SFSIG.HAS_SIGNATURE_VALUE);
if (ni2.hasNext()) {
signatureValue = ni2.next().asLiteral().toString();
}
return signatureValue;
}
public static WonSignatureData extractWonSignatureData(final String uri, final Model model) {
return extractWonSignatureData(model.getResource(uri));
}
public static WonSignatureData extractWonSignatureData(final Resource resource) {
Statement stmt = resource.getRequiredProperty(WONMSG.HAS_SIGNED_GRAPH_PROPERTY);
String signedGraphUri = stmt.getObject().asResource().getURI();
stmt = resource.getRequiredProperty(SFSIG.HAS_SIGNATURE_VALUE);
String signatureValue = stmt.getObject().asLiteral().getString();
stmt = resource.getRequiredProperty(WONMSG.HAS_HASH_PROPERTY);
String hash = stmt.getObject().asLiteral().getString();
stmt = resource.getRequiredProperty(WONMSG.HAS_PUBLIC_KEY_FINGERPRINT_PROPERTY);
String fingerprint = stmt.getObject().asLiteral().getString();
stmt = resource.getRequiredProperty(SFSIG.HAS_VERIFICATION_CERT);
String cert = stmt.getObject().asResource().getURI();
return new WonSignatureData(signedGraphUri, resource.getURI(), signatureValue, hash, fingerprint,
cert);
}
/**
* Adds the triples holding the signature data to the model of the specified resource, using the resource as the
* subject.
* @param subject
* @param wonSignatureData
*/
public static void addSignature(Resource subject, WonSignatureData wonSignatureData){
assert wonSignatureData.getHash() != null;
assert wonSignatureData.getSignatureValue() != null;
assert wonSignatureData.getPublicKeyFingerprint() != null;
assert wonSignatureData.getSignedGraphUri() != null;
assert wonSignatureData.getVerificationCertificateUri() != null;
Model containingGraph = subject.getModel();
subject.addProperty(RDF.type, SFSIG.SIGNATURE);
subject.addProperty(WONMSG.HAS_HASH_PROPERTY, wonSignatureData.getHash());
subject.addProperty(SFSIG.HAS_SIGNATURE_VALUE, wonSignatureData.getSignatureValue());
subject.addProperty(WONMSG.HAS_SIGNED_GRAPH_PROPERTY,
containingGraph.createResource(wonSignatureData.getSignedGraphUri()));
subject.addProperty(WONMSG.HAS_PUBLIC_KEY_FINGERPRINT_PROPERTY, wonSignatureData.getPublicKeyFingerprint());
subject.addProperty(SFSIG.HAS_VERIFICATION_CERT, containingGraph.createResource(wonSignatureData
.getVerificationCertificateUri()));
}
}
public static class WonNodeUtils
{
/**
* Creates a WonNodeInfo object based on the specified dataset. The first model
* found in the dataset that seems to contain the data needed for a WonNodeInfo
* object is used.
* @param wonNodeUri
* @param dataset
* @return
*/
public static WonNodeInfo getWonNodeInfo(final URI wonNodeUri, Dataset dataset){
assert wonNodeUri != null: "wonNodeUri must not be null";
assert dataset != null: "dataset must not be null";
return RdfUtils.findFirst(dataset, new RdfUtils.ModelVisitor<WonNodeInfo>()
{
@Override
public WonNodeInfo visit(final Model model) {
//use the first blank node found for [wonNodeUri] won:hasUriPatternSpecification [blanknode]
NodeIterator it = model.listObjectsOfProperty(model.getResource(wonNodeUri.toString()),
WON.HAS_URI_PATTERN_SPECIFICATION);
if (!it.hasNext()) return null;
WonNodeInfoBuilder wonNodeInfoBuilder = new WonNodeInfoBuilder();
wonNodeInfoBuilder.setWonNodeURI(wonNodeUri.toString());
RDFNode node = it.next();
// set the URI prefixes
it = model.listObjectsOfProperty(node.asResource(), WON.HAS_NEED_URI_PREFIX);
if (! it.hasNext() ) return null;
String needUriPrefix = it.next().asLiteral().getString();
wonNodeInfoBuilder.setNeedURIPrefix(needUriPrefix);
it = model.listObjectsOfProperty(node.asResource(), WON.HAS_CONNECTION_URI_PREFIX);
if (! it.hasNext() ) return null;
wonNodeInfoBuilder.setConnectionURIPrefix(it.next().asLiteral().getString());
it = model.listObjectsOfProperty(node.asResource(), WON.HAS_EVENT_URI_PREFIX);
if (! it.hasNext() ) return null;
wonNodeInfoBuilder.setEventURIPrefix(it.next().asLiteral().getString());
// set the need list URI
it = model.listObjectsOfProperty(model.getResource(wonNodeUri.toString()), WON.HAS_NEED_LIST);
if (it.hasNext() ) {
wonNodeInfoBuilder.setNeedListURI(it.next().asNode().getURI());
} else {
wonNodeInfoBuilder.setNeedListURI(needUriPrefix);
}
// set the supported protocol implementations
String queryString = "SELECT ?protocol ?param ?value WHERE { ?a <%s> ?c. " +
"?c <%s> ?protocol. ?c ?param ?value. FILTER ( ?value != ?protocol ) }";
queryString = String.format(queryString, WON.SUPPORTS_WON_PROTOCOL_IMPL.toString(), RDF.getURI() + "type");
Query protocolQuery = QueryFactory.create(queryString);
QueryExecution qexec = QueryExecutionFactory.create(protocolQuery, model);
ResultSet rs = qexec.execSelect();
while (rs.hasNext()) {
QuerySolution qs = rs.nextSolution();
String protocol = rdfNodeToString(qs.get("protocol"));
String param = rdfNodeToString(qs.get("param"));
String value = rdfNodeToString(qs.get("value"));
wonNodeInfoBuilder.addSupportedProtocolImplParamValue(protocol, param, value);
}
return wonNodeInfoBuilder.build();
}
});
}
private static String rdfNodeToString(RDFNode node) {
if (node.isLiteral()) {
return node.asLiteral().getString();
} else if (node.isResource()) {
return node.asResource().getURI();
}
return null;
}
}
public static class MessageUtils
{
/**
* Adds the specified text as a won:hasTextMessage to the model's base resource.
* @param message
* @return
*/
public static Model addMessage(Model model, String message) {
Resource baseRes = RdfUtils.findOrCreateBaseResource(model);
baseRes.addProperty(WON.HAS_TEXT_MESSAGE, message, XSDDatatype.XSDstring);
return model;
}
/**
* Creates an RDF model containing a text message.
* @param message
* @return
*/
public static Model textMessage(String message) {
Model messageModel = createModelWithBaseResource();
Resource baseRes = messageModel.createResource(messageModel.getNsPrefixURI(""));
baseRes.addProperty(WON.HAS_TEXT_MESSAGE,message, XSDDatatype.XSDstring);
return messageModel;
}
/**
* Creates an RDF model containing a generic message.
*
* @return
*/
public static Model genericMessage(URI predicate, URI object) {
return genericMessage(new PropertyImpl(predicate.toString()), new ResourceImpl(object.toString()));
}
/**
* Creates an RDF model containing a generic message.
*
* @return
*/
public static Model genericMessage(Property predicate, Resource object) {
Model messageModel = createModelWithBaseResource();
Resource baseRes = RdfUtils.getBaseResource(messageModel);
baseRes.addProperty(RDF.type, WONMSG.TYPE_CONNECTION_MESSAGE);
baseRes.addProperty(predicate, object);
return messageModel;
}
/**
* Creates an RDF model containing a feedback message referring to the specified resource
* that is either positive or negative.
* @return
*/
public static Model binaryFeedbackMessage(URI forResource, boolean isFeedbackPositive) {
Model messageModel = createModelWithBaseResource();
Resource baseRes = RdfUtils.getBaseResource(messageModel);
Resource feedbackNode = messageModel.createResource();
baseRes.addProperty(WON.HAS_FEEDBACK, feedbackNode);
feedbackNode.addProperty(WON.HAS_BINARY_RATING, isFeedbackPositive ? WON.GOOD : WON.BAD);
feedbackNode.addProperty(WON.FOR_RESOURCE, messageModel.createResource(forResource.toString()));
return messageModel;
}
/**
* Returns the first won:hasTextMessage object, or null if none is found.
* Won't work on WonMessage models, removal depends on refactoring of BA facet code
* @param model
* @return
*/
@Deprecated
public static String getTextMessage(Model model){
Statement stmt = model.getProperty(RdfUtils.getBaseResource(model), WON.HAS_TEXT_MESSAGE);
if (stmt != null) {
return stmt.getObject().asLiteral().getLexicalForm();
}
return null;
}
/**
* Returns the first won:hasTextMessage object, or null if none is found.
* @param wonMessage
* @return
*/
public static String getTextMessage(final WonMessage wonMessage){
return RdfUtils.findFirst(wonMessage.getCompleteDataset(), new RdfUtils.ModelVisitor<String>() {
@Override
public String visit(Model model) {
Statement stmt = model.getProperty(model.getResource(wonMessage.getMessageURI().toString()), WON.HAS_TEXT_MESSAGE);
if (stmt != null) {
return stmt.getObject().asLiteral().getLexicalForm();
}
URI remoteMessageURI = wonMessage.getCorrespondingRemoteMessageURI();
if (remoteMessageURI != null){
stmt = model.getProperty(model.getResource(remoteMessageURI.toString()), WON.HAS_TEXT_MESSAGE);
if (stmt != null) {
return stmt.getObject().asLiteral().getLexicalForm();
}
}
return null;
}
});
}
/**
* Converts the specified hint message into a Match object.
* @param wonMessage
* @return a match object or null if the message is not a hint message.
*/
public static Match toMatch(final WonMessage wonMessage) {
if (!WONMSG.TYPE_HINT.equals(wonMessage.getMessageType().getResource())){
return null;
}
Match match = new Match();
match.setFromNeed(wonMessage.getReceiverNeedURI());
Dataset messageContent = wonMessage.getMessageContent();
RDFNode score = RdfUtils.findOnePropertyFromResource(messageContent, wonMessage.getMessageURI(),
WON.HAS_MATCH_SCORE);
if (!score.isLiteral()) return null;
match.setScore(score.asLiteral().getDouble());
RDFNode counterpart = RdfUtils.findOnePropertyFromResource(messageContent, wonMessage.getMessageURI(),
WON.HAS_MATCH_COUNTERPART);
if (!counterpart.isResource()) return null;
match.setToNeed(URI.create(counterpart.asResource().getURI()));
return match;
}
public static WonMessage copyByDatasetSerialization(final WonMessage toWrap) {
WonMessage copied = new WonMessage(RdfUtils.readDatasetFromString(
RdfUtils.writeDatasetToString(toWrap.getCompleteDataset(),
Lang.TRIG) ,Lang.TRIG));
return copied;
}
}
public static class FacetUtils {
public static URI getFacet(WonMessage message){
URI uri = getObjectOfMessageProperty(message, WON.HAS_FACET);
if (uri == null) {
uri = getObjectOfRemoteMessageProperty(message, WON.HAS_REMOTE_FACET);
}
return uri;
}
public static URI getRemoteFacet(WonMessage message) {
URI uri = getObjectOfMessageProperty(message, WON.HAS_REMOTE_FACET);
if (uri == null) {
uri = getObjectOfRemoteMessageProperty(message, WON.HAS_FACET);
}
return uri;
}
/**
* Returns a property of the message (i.e. the object of the first triple ( [message-uri] [property] X )
* found in one of the content graphs of the specified message.
*/
private static URI getObjectOfMessageProperty(final WonMessage message, final Property property) {
List<String> contentGraphUris = message.getContentGraphURIs();
Dataset contentGraphs = message.getMessageContent();
URI messageURI = message.getMessageURI();
for (String graphUri: contentGraphUris) {
Model contentGraph = contentGraphs.getNamedModel(graphUri);
StmtIterator smtIter = contentGraph.getResource(messageURI.toString()).listProperties(property);
if (smtIter.hasNext()) {
return URI.create(smtIter.nextStatement().getObject().asResource().getURI());
}
}
return null;
}
/**
* Returns a property of the corresponding remote message (i.e. the object of the first triple (
* [corresponding-remote-message-uri] [property] X )
* found in one of the content graphs of the specified message.
*/
private static URI getObjectOfRemoteMessageProperty(final WonMessage message, final Property property) {
List<String> contentGraphUris = message.getContentGraphURIs();
Dataset contentGraphs = message.getMessageContent();
URI messageURI = message.getCorrespondingRemoteMessageURI();
if (messageURI != null) {
for (String graphUri : contentGraphUris) {
Model contentGraph = contentGraphs.getNamedModel(graphUri);
StmtIterator smtIter = contentGraph.getResource(messageURI.toString()).listProperties(property);
if (smtIter.hasNext()) {
return URI.create(smtIter.nextStatement().getObject().asResource().getURI());
}
}
}
return null;
}
/**
* Returns all facets found in the model, attached to the null relative URI '<>'.
* Returns an empty collection if there is no such facet.
* @param content
* @return
*/
public static Collection<URI> getFacets(Model content) {
Resource baseRes = RdfUtils.getBaseResource(content);
StmtIterator stmtIterator = baseRes.listProperties(WON.HAS_FACET);
LinkedList<URI> ret = new LinkedList<URI>();
while (stmtIterator.hasNext()){
RDFNode object = stmtIterator.nextStatement().getObject();
if (object.isURIResource()){
ret.add(URI.create(object.asResource().getURI()));
}
}
return ret;
}
/**
* Adds a triple to the model of the form <> won:hasFacet [facetURI].
* @param content
* @param facetURI
*/
public static void addFacet(final Model content, final URI facetURI)
{
Resource baseRes = RdfUtils.getBaseResource(content);
baseRes.addProperty(WON.HAS_FACET, content.createResource(facetURI.toString()));
}
/**
* Adds a triple to the model of the form <> won:hasRemoteFacet [facetURI].
* @param content
* @param facetURI
*/
public static void addRemoteFacet(final Model content, final URI facetURI)
{
Resource baseRes = RdfUtils.getBaseResource(content);
baseRes.addProperty(WON.HAS_REMOTE_FACET, content.createResource(facetURI.toString()));
}
/**
* Creates a model for connecting two facets.CONNECTED.getURI().equals(connectionState)
* @return
*/
public static Model createFacetModelForHintOrConnect(URI facet, URI remoteFacet)
{
Model model = ModelFactory.createDefaultModel();
Resource baseResource = findOrCreateBaseResource(model);
WonRdfUtils.FacetUtils.addFacet(model, facet);
WonRdfUtils.FacetUtils.addRemoteFacet(model, remoteFacet);
logger.debug("facet model contains these facets: from:{} to:{}", facet, remoteFacet);
return model;
}
}
public static class ConnectionUtils {
public static boolean isConnected(Dataset connectionDataset, URI connectionUri) {
URI connectionState = getConnectionState(connectionDataset, connectionUri);
return ConnectionState.CONNECTED.getURI().equals(connectionState);
}
public static URI getConnectionState(Dataset connectionDataset, URI connectionUri) {
Path statePath = PathParser.parse("won:hasConnectionState", DefaultPrefixUtils.getDefaultPrefixes());
return RdfUtils.getURIPropertyForPropertyPath(connectionDataset, connectionUri, statePath);
}
/**
* return the needURI of a connection
*
* @param dataset <code>Dataset</code> object which contains connection information
* @param connectionURI
* @return <code>URI</code> of the need
*/
public static URI getLocalNeedURIFromConnection(Dataset dataset, final URI connectionURI) {
return URI.create(RdfUtils.findOnePropertyFromResource(
dataset, connectionURI, WON.BELONGS_TO_NEED).asResource().getURI());
}
public static URI getRemoteNeedURIFromConnection(Dataset dataset, final URI connectionURI) {
return URI.create(RdfUtils.findOnePropertyFromResource(
dataset, connectionURI, WON.HAS_REMOTE_NEED).asResource().getURI());
}
public static URI getWonNodeURIFromConnection(Dataset dataset, final URI connectionURI) {
return URI.create(RdfUtils.findOnePropertyFromResource(
dataset, connectionURI, WON.HAS_WON_NODE).asResource().getURI());
}
public static URI getRemoteConnectionURIFromConnection(Dataset dataset, final URI connectionURI) {
return URI.create(RdfUtils.findOnePropertyFromResource(
dataset, connectionURI, WON.HAS_REMOTE_CONNECTION).asResource().getURI());
}
}
private static Model createModelWithBaseResource() {
Model model = ModelFactory.createDefaultModel();
model.setNsPrefix("", "no:uri");
model.createResource(model.getNsPrefixURI(""));
return model;
}
public static class NeedUtils
{
/**
* searches for a subject of type won:Need and returns the NeedURI
*
* @param dataset <code>Dataset</code> object which will be searched for the NeedURI
* @return <code>URI</code> which is of type won:Need
*/
public static URI getNeedURI(Dataset dataset) {
return RdfUtils.findOne(dataset, new RdfUtils.ModelVisitor<URI>()
{
@Override
public URI visit(final Model model) {
return getNeedURI(model);
}
}, true);
}
/**
* searches for a subject of type won:Need and returns the NeedURI
*
* @param model <code>Model</code> object which will be searched for the NeedURI
* @return <code>URI</code> which is of type won:Need
*/
public static URI getNeedURI(Model model) {
Resource res = getNeedResource(model);
return res == null ? null : URI.create(res.getURI());
}
/**
* searches for a subject of type won:Need and returns the NeedURI
*
* @param model <code>Model</code> object which will be searched for the NeedURI
* @return <code>URI</code> which is of type won:Need
*/
public static Resource getNeedResource(Model model) {
List<Resource> needURIs = new ArrayList<>();
ResIterator iterator = model.listSubjectsWithProperty(RDF.type, WON.NEED);
while (iterator.hasNext()) {
needURIs.add(iterator.next());
}
if (needURIs.size() == 0)
return null;
else if (needURIs.size() == 1)
return needURIs.get(0);
else if (needURIs.size() > 1) {
Resource u = needURIs.get(0);
for (Resource uri : needURIs) {
if (!uri.equals(u))
throw new IncorrectPropertyCountException(1,2);
}
return u;
}
else
return null;
}
public static URI getWonNodeURIFromNeed(Dataset dataset, final URI needURI) {
return URI.create(RdfUtils.findOnePropertyFromResource(
dataset, needURI, WON.HAS_WON_NODE).asResource().getURI());
}
}
}