All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.jsonldjava.impl.TurtleTripleCallback Maven / Gradle / Ivy
package com.github.jsonldjava.impl;
import static com.github.jsonldjava.core.JsonLdConsts.RDF_FIRST;
import static com.github.jsonldjava.core.JsonLdConsts.RDF_NIL;
import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST;
import static com.github.jsonldjava.core.JsonLdConsts.XSD_BOOLEAN;
import static com.github.jsonldjava.core.JsonLdConsts.XSD_DOUBLE;
import static com.github.jsonldjava.core.JsonLdConsts.XSD_FLOAT;
import static com.github.jsonldjava.core.JsonLdConsts.XSD_INTEGER;
import static com.github.jsonldjava.core.JsonLdConsts.XSD_STRING;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.github.jsonldjava.core.JsonLdTripleCallback;
import com.github.jsonldjava.core.RDFDataset;
public class TurtleTripleCallback implements JsonLdTripleCallback {
private static final int MAX_LINE_LENGTH = 160;
private static final int TAB_SPACES = 4;
private static final String COLS_KEY = "..cols.."; // this shouldn't be a
// valid iri/bnode i
// hope!
final Map availableNamespaces = new LinkedHashMap() {
{
// TODO: fill with default namespaces
}
};
Set usedNamespaces;
public TurtleTripleCallback() {
}
@Override
public Object call(RDFDataset dataset) {
for (final Entry e : dataset.getNamespaces().entrySet()) {
availableNamespaces.put(e.getValue(), e.getKey());
}
usedNamespaces = new LinkedHashSet();
final int tabs = 0;
final Map> refs = new LinkedHashMap>();
final Map>> ttl = new LinkedHashMap>>();
for (String graphName : dataset.keySet()) {
final List triples = dataset.getQuads(graphName);
if ("@default".equals(graphName)) {
graphName = null;
}
// http://www.w3.org/TR/turtle/#unlabeled-bnodes
// TODO: implement nesting for unlabled nodes
// map of what the output should look like
// subj (or [ if bnode) > pred > obj
// > obj (set ref if IRI)
// > pred > obj (set ref if bnode)
// subj > etc etc etc
// subjid -> [ ref, ref, ref ]
String prevSubject = "";
String prevPredicate = "";
Map> thisSubject = null;
List thisPredicate = null;
for (final RDFDataset.Quad triple : triples) {
final String subject = triple.getSubject().getValue();
final String predicate = triple.getPredicate().getValue();
if (prevSubject.equals(subject)) {
if (prevPredicate.equals(predicate)) {
// nothing to do
} else {
// new predicate
if (thisSubject.containsKey(predicate)) {
thisPredicate = thisSubject.get(predicate);
} else {
thisPredicate = new ArrayList();
thisSubject.put(predicate, thisPredicate);
}
prevPredicate = predicate;
}
} else {
// new subject
if (ttl.containsKey(subject)) {
thisSubject = ttl.get(subject);
} else {
thisSubject = new LinkedHashMap>();
ttl.put(subject, thisSubject);
}
if (thisSubject.containsKey(predicate)) {
thisPredicate = thisSubject.get(predicate);
} else {
thisPredicate = new ArrayList();
thisSubject.put(predicate, thisPredicate);
}
prevSubject = subject;
prevPredicate = predicate;
}
if (triple.getObject().isLiteral()) {
thisPredicate.add(triple.getObject());
} else {
final String o = triple.getObject().getValue();
if (o.startsWith("_:")) {
// add ref to o
if (!refs.containsKey(o)) {
refs.put(o, new ArrayList());
}
refs.get(o).add(thisPredicate);
}
thisPredicate.add(o);
}
}
}
final Map> collections = new LinkedHashMap>();
final List subjects = new ArrayList(ttl.keySet());
// find collections
for (final String subj : subjects) {
Map> preds = ttl.get(subj);
if (preds != null && preds.containsKey(RDF_FIRST)) {
final List col = new ArrayList();
collections.put(subj, col);
while (true) {
final List first = preds.remove(RDF_FIRST);
final Object o = first.get(0);
col.add(o);
// refs
if (refs.containsKey(o)) {
refs.get(o).remove(first);
refs.get(o).add(col);
}
final String next = (String) preds.remove(RDF_REST).get(0);
if (RDF_NIL.equals(next)) {
// end of this list
break;
}
// if collections already contains a value for "next", add
// it to this col and break out
if (collections.containsKey(next)) {
col.addAll(collections.remove(next));
break;
}
preds = ttl.remove(next);
refs.remove(next);
}
}
}
// process refs (nesting referenced bnodes if only one reference to them
// in the whole graph)
for (final String id : refs.keySet()) {
// skip items if there is more than one reference to them in the
// graph
if (refs.get(id).size() > 1) {
continue;
}
// otherwise embed them into the referenced location
Object object = ttl.remove(id);
if (collections.containsKey(id)) {
object = new LinkedHashMap>();
final List tmp = new ArrayList();
tmp.add(collections.remove(id));
((HashMap) object).put(COLS_KEY, tmp);
}
final List predicate = (List) refs.get(id).get(0);
// replace the one bnode ref with the object
predicate.set(predicate.lastIndexOf(id), object);
}
// replace the rest of the collections
for (final String id : collections.keySet()) {
final Map> subj = ttl.get(id);
if (!subj.containsKey(COLS_KEY)) {
subj.put(COLS_KEY, new ArrayList());
}
subj.get(COLS_KEY).add(collections.get(id));
}
// build turtle output
final String output = generateTurtle(ttl, 0, 0, false);
String prefixes = "";
for (final String prefix : usedNamespaces) {
final String name = availableNamespaces.get(prefix);
prefixes += "@prefix " + name + ": <" + prefix + "> .\n";
}
return ("".equals(prefixes) ? "" : prefixes + "\n") + output;
}
private String generateObject(Object object, String sep, boolean hasNext, int indentation,
int lineLength) {
String rval = "";
String obj;
if (object instanceof String) {
obj = getURI((String) object);
} else if (object instanceof RDFDataset.Literal) {
obj = ((RDFDataset.Literal) object).getValue();
final String lang = ((RDFDataset.Literal) object).getLanguage();
final String dt = ((RDFDataset.Literal) object).getDatatype();
if (lang != null) {
obj = "\"" + obj + "\"";
obj += "@" + lang;
} else if (dt != null) {
// TODO: this probably isn't an exclusive list of all the
// datatype literals that can be represented as native types
if (!(XSD_DOUBLE.equals(dt) || XSD_INTEGER.equals(dt) || XSD_FLOAT.equals(dt) || XSD_BOOLEAN
.equals(dt))) {
obj = "\"" + obj + "\"";
if (!XSD_STRING.equals(dt)) {
obj += "^^" + getURI(dt);
}
}
} else {
obj = "\"" + obj + "\"";
}
} else {
// must be an object
final Map>> tmp = new LinkedHashMap>>();
tmp.put("_:x", (Map>) object);
obj = generateTurtle(tmp, indentation + 1, lineLength, true);
}
final int idxofcr = obj.indexOf("\n");
// check if output will fix in the max line length (factor in comma if
// not the last item, current line length and length to the next CR)
if ((hasNext ? 1 : 0) + lineLength + (idxofcr != -1 ? idxofcr : obj.length()) > MAX_LINE_LENGTH) {
rval += "\n" + tabs(indentation + 1);
lineLength = (indentation + 1) * TAB_SPACES;
}
rval += obj;
if (idxofcr != -1) {
lineLength += (obj.length() - obj.lastIndexOf("\n"));
} else {
lineLength += obj.length();
}
if (hasNext) {
rval += sep;
lineLength += sep.length();
if (lineLength < MAX_LINE_LENGTH) {
rval += " ";
lineLength++;
} else {
rval += "\n";
}
}
return rval;
}
private String generateTurtle(Map>> ttl, int indentation,
int lineLength, boolean isObject) {
String rval = "";
final Iterator subjIter = ttl.keySet().iterator();
while (subjIter.hasNext()) {
final String subject = subjIter.next();
final Map> subjval = ttl.get(subject);
// boolean isBlankNode = subject.startsWith("_:");
boolean hasOpenBnodeBracket = false;
if (subject.startsWith("_:")) {
// only open blank node bracket the node doesn't contain any
// collections
if (!subjval.containsKey(COLS_KEY)) {
rval += "[ ";
lineLength += 2;
hasOpenBnodeBracket = true;
}
// TODO: according to http://www.rdfabout.com/demo/validator/
// 1) collections as objects cannot contain any predicates other
// than rdf:first and rdf:rest
// 2) collections cannot be surrounded with [ ]
// check for collection
if (subjval.containsKey(COLS_KEY)) {
final List collections = subjval.remove(COLS_KEY);
for (final Object collection : collections) {
rval += "( ";
lineLength += 2;
final Iterator objIter = ((List) collection).iterator();
while (objIter.hasNext()) {
final Object object = objIter.next();
rval += generateObject(object, "", objIter.hasNext(), indentation,
lineLength);
lineLength = rval.length() - rval.lastIndexOf("\n");
}
rval += " ) ";
lineLength += 3;
}
}
// check for blank node
} else {
rval += getURI(subject) + " ";
lineLength += subject.length() + 1;
}
final Iterator predIter = ttl.get(subject).keySet().iterator();
while (predIter.hasNext()) {
final String predicate = predIter.next();
rval += getURI(predicate) + " ";
lineLength += predicate.length() + 1;
final Iterator objIter = ttl.get(subject).get(predicate).iterator();
while (objIter.hasNext()) {
final Object object = objIter.next();
rval += generateObject(object, ",", objIter.hasNext(), indentation, lineLength);
lineLength = rval.length() - rval.lastIndexOf("\n");
}
if (predIter.hasNext()) {
rval += " ;\n" + tabs(indentation + 1);
lineLength = (indentation + 1) * TAB_SPACES;
}
}
if (hasOpenBnodeBracket) {
rval += " ]";
}
if (!isObject) {
rval += " .\n";
if (subjIter.hasNext()) { // add blank space if we have another
// object below this
rval += "\n";
}
}
}
return rval;
}
// TODO: Assert (TAB_SPACES == 4) otherwise this needs to be edited, and
// should fail to compile
private String tabs(int tabs) {
String rval = "";
for (int i = 0; i < tabs; i++) {
rval += " "; // using spaces for tabs
}
return rval;
}
/**
* checks the URI for a prefix, and if one is found, set used prefixes to
* true
*
* @param predicate
* @return
*/
private String getURI(String uri) {
// check for bnode
if (uri.startsWith("_:")) {
// return the bnode id
return uri;
}
for (final String prefix : availableNamespaces.keySet()) {
if (uri.startsWith(prefix)) {
usedNamespaces.add(prefix);
// return the prefixed URI
return availableNamespaces.get(prefix) + ":" + uri.substring(prefix.length());
}
}
// return the full URI
return "<" + uri + ">";
}
}