package won.cryptography.rdfsign; import org.apache.jena.query.Dataset; import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; import de.uni_koblenz.aggrimm.icp.crypto.sign.graph.GraphCollection; import de.uni_koblenz.aggrimm.icp.crypto.sign.graph.NamedGraph; import de.uni_koblenz.aggrimm.icp.crypto.sign.graph.Prefix; import de.uni_koblenz.aggrimm.icp.crypto.sign.graph.Triple; import de.uni_koblenz.aggrimm.icp.crypto.sign.trigplus.TriGPlusWriter; import org.apache.jena.riot.RDFDataMgr; import org.apache.jena.riot.RDFFormat; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.util.List; import java.util.Map; /** * Created by ypanchenko on 09.07.2014. */ public class ModelConverter { /** * Converts Signingframework's NamedGraph into Jena's Model. * * It is required, that GraphCollection stores the prefixes (if present), * but that they are not applied (i.e. its applyPrefixes() method should not * be called). Otherwise there could be errors when converting, since * Signingframework when applying prefixes just looks whether the resource * uri starts with that prefix uri, which would be true in both cases below: * @prefix : <http://www.example.com/resource/need/12#> . * @prefix need: <http://www.example.com/resource/need/12> . * Also, applying prefixes in NamedGraph in cases like * @prefix : <http://www.example.com/resource/need/12/> * <http://www.example.com/resource/need/12/connections/> a ldp:Container . * would result in a wrong RDF triple: * :connections/ a ldp:Container . */ public static Model namedGraphToModel(String graphName, GraphCollection gc) throws Exception { NamedGraph graph = null; for (NamedGraph g : gc.getGraphs()) { if (g.getName().equals(graphName)) { graph = g; break; } } return namedGraphToModel(graph, gc.getPrefixes()); } /** * Converts Jena's Model into Signingframework's NamedGraph of GraphCollection. * * Resulting GraphCollection contains exactly one NamedGraph that corresponds to * the provided Model (modelURI) from the provided Dataset. The resulting * GraphCollection stores the prefixes (if were present in the Datastore), * but they are not applied (for reasons, see namedGraphToModel() doc). */ public static GraphCollection modelToGraphCollection(String modelURI, Dataset modelDataset) { Map<String, String> pm = modelDataset.getDefaultModel().getNsPrefixMap(); Model model = modelDataset.getNamedModel(modelURI); return modelToGraphCollection(modelURI, model, pm); } private static Model namedGraphToModel(NamedGraph graph, List<Prefix> prefixes) throws Exception { // Here, the simplest, but probably not the most efficient, approach is // applied: Signingframework's reader and Jena's writer are used to // transform data from one data structure to another. Works fine. ByteArrayOutputStream os = new ByteArrayOutputStream(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os)); String dfPref = ""; // write prefixes for (Prefix p : prefixes) { writer.write(p.toString()); if (p.getPrefix().equals(":")) { dfPref = p.getIriContent(); } } // write NamedGraph TriGPlusWriter.writeGraph(writer, graph, 3); writer.close(); String content = os.toString(); // read the result with Jena as Dataset ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); Dataset dataset = DatasetFactory.createGeneral(); RDFDataMgr.read(dataset, is, RDFFormat.TRIG.getLang()); // extract the Model that corresponds to the NamedGraph String modelName = graphNameToModelName(graph.getName(), dfPref); Model model = dataset.getNamedModel(modelName); for (Prefix pref : prefixes) { model.setNsPrefix(pref.getPrefix().replace(":", ""), pref.getIriContent()); } return model; } private static String graphNameToModelName(final String graphName, final String dfPref) { if (graphName.startsWith("<")) { return graphName.substring(1, graphName.length() - 1); } else { return dfPref + graphName.replace(":", ""); } } private static GraphCollection modelToGraphCollection(String name, Model model, Map<String, String> pm) { // Convert each subj pred obj in Jena Statement into String and add to // SigningFramework's NamedGraph. //The simpler approach with just using Jena's writer and Signingframework's //reader to transform data between data structures won't work since //Signingframework has problems with recognizing the [] structure GraphCollection graphc = new GraphCollection(); NamedGraph namedGraph = new NamedGraph(enclose(name, "<", ">"), 0, null); StmtIterator iterator = model.listStatements(); while (iterator.hasNext()) { Statement stmt = iterator.nextStatement(); String subjString = rdfNodeAsString(stmt.getSubject()); String objString = rdfNodeAsString(stmt.getObject()); String predString = enclose(stmt.getPredicate().asResource().getURI(), "<", ">"); Triple gcTriple = new Triple(subjString, predString, objString); namedGraph.addTriple(gcTriple); } graphc.addGraph(namedGraph); for (String prefix : pm.keySet()) { graphc.addPrefix(new Prefix(prefix + ":", "<" + pm.get(prefix) + ">")); } // don't apply prefixes since it can result in funny things like: // pref:/connections/, and also the collision on the prefix uris // that starts the same. E.g. having prefixes below in need rdf // would cause errors // @prefix : <http://www.example.com/resource/need/12#> . // @prefix need: <http://www.example.com/resource/need/12> . //graphc.applyPrefixes(); return graphc; } private static String rdfNodeAsString(final RDFNode rdfNode) { String result = null; if (rdfNode.isURIResource()) { String uri = rdfNode.asResource().getURI(); result = enclose(uri, "<", ">"); } else if (rdfNode.isLiteral()) { result = enclose(rdfNode.asLiteral().getLexicalForm(), "\"", "\""); if (rdfNode.asLiteral().getDatatypeURI() != null) { result = enclose(result, "", "^^" + "<" + rdfNode.asLiteral().getDatatypeURI() + ">"); } else if (rdfNode.asLiteral().getLanguage() != null && !rdfNode.asLiteral().getLanguage().isEmpty()) { result = enclose(result, "", "@" + rdfNode.asLiteral().getLanguage()); } } else if (rdfNode.isAnon()) { result = enclose(rdfNode.asResource().getId().getLabelString(), "_:", ""); } else { // TODO It might need to be improved as some syntax cases might not be covered so far // a collection?? throw new UnsupportedOperationException("support missing for converting: " + rdfNode.toString()); } return result; } private static String enclose(String string, String start, String end) { return start + string + end; } }