package edu.neu.ccs.task.rdf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import com.hp.hpl.jena.rdf.model.AnonId;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
public class RdfInterface {
private static final long serialVersionUID = 1L;
public static void setup(TaskEngineWithRdf engine) {
Scriptable env = engine.getGlobalEnv();
env.put("_temp_", env, engine);
String cname = "Packages." + RdfInterface.class.getName();
try {
engine.eval("$rdf = new " + cname + "(_temp_);", env,
"RdfInterface setup");
} finally {
env.delete("_temp_");
}
}
private final TaskEngineWithRdf engine;
private final Model rdf;
public RdfInterface(TaskEngineWithRdf engine) {
this.engine = engine;
this.rdf = engine.getRdfModel();
}
public void load(String id) throws IOException {
engine.loadRuntimeRdfModel(id);
}
public void save() throws IOException {
engine.saveRuntimeRdfModel();
}
public void saveBackup(int index) throws IOException {
engine.saveRuntimeRdfModelBackup(index);
}
private abstract class Call extends ScriptableObject implements Function {
private static final long serialVersionUID = 1L;
private final String name;
protected Call(String name) {
this.name = name;
}
@Override
public String getClassName() {
return name;
}
public Scriptable construct(Context arg0, Scriptable arg1, Object[] arg2) {
throw new UnsupportedOperationException();
}
}
private abstract class QueryCall extends Call {
private static final long serialVersionUID = 1L;
protected QueryCall(String name) { super(name); }
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = null;
if ((args.length > 0) && (args[0] != null))
subject = getResource(args[0].toString());
Property predicate = null;
if ((args.length > 1) && (args[1] != null))
predicate = getPredicate(args[1].toString());
Resource objR = null;
Literal objL = null;
if ((args.length > 2) && args[2] != null) {
String object = args[2].toString();
objR = getResource(object);
objL = rdf.createLiteral(object);
}
return handleQuery(cx, scope, subject, predicate, objR, objL);
}
protected abstract Object handleQuery(Context cx, Scriptable scope,
Resource subject, Property predicate,
Resource objR, Literal objL);
}
public final Call get = new QueryCall("$rdf.get") {
private static final long serialVersionUID = 1L;
protected Object handleQuery(Context cx, Scriptable scope,
Resource subject, Property predicate,
Resource objR, Literal objL) {
List<Scriptable> triples = new ArrayList<Scriptable>();
ExtendedIterator<Statement> si;
if ((objR != null) && (objL != null)) {
StmtIterator i1 = rdf.listStatements(subject, predicate, objR);
StmtIterator i2 = rdf.listStatements(subject, predicate, objL);
si = i1.andThen(i2);
} else if (objR != null) {
si = rdf.listStatements(subject, predicate, objR);
} else {
// this also handles no object specified (objR=objL=null)
si = rdf.listStatements(subject, predicate, objL);
}
while (si.hasNext())
triples.add(asJsObject(si.next(), cx, scope));
si.close();
return cx.newArray(scope, triples.toArray());
}
};
public final Call getR = new QueryCall("$rdf.getR") {
private static final long serialVersionUID = 1L;
protected Object handleQuery(Context cx, Scriptable scope,
Resource subject, Property predicate,
Resource objR, Literal objL) {
List<Scriptable> triples = new ArrayList<Scriptable>();
StmtIterator si = rdf.listStatements(subject, predicate, objR);
while (si.hasNext()) {
Statement s = si.next();
if (s.getObject().isResource())
triples.add(asJsObject(s, cx, scope));
}
si.close();
return cx.newArray(scope, triples.toArray());
}
};
public final Call getL = new QueryCall("$rdf.getL") {
private static final long serialVersionUID = 1L;
protected Object handleQuery(Context cx, Scriptable scope,
Resource subject, Property predicate,
Resource objR, Literal objL) {
List<Scriptable> triples = new ArrayList<Scriptable>();
StmtIterator si = rdf.listStatements(subject, predicate, objR);
while (si.hasNext()) {
Statement s = si.next();
if (s.getObject().isLiteral())
triples.add(asJsObject(s, cx, scope));
}
si.close();
return cx.newArray(scope, triples.toArray());
}
};
public final Call ask = new QueryCall("$rdf.ask") {
private static final long serialVersionUID = 1L;
protected Object handleQuery(Context cx, Scriptable scope,
Resource subj, Property pred,
Resource objR, Literal objL) {
if ((objR == null) && (objL == null))
return rdf.contains(subj, pred);
return ((objR != null) && rdf.contains(subj, pred, objR)) ||
((objL != null) && rdf.contains(subj, pred, objL));
}
};
public final Call getOneR = new Call("$rdf.getOneR") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
NodeIterator it = rdf.listObjectsOfProperty(subject, predicate);
while (it.hasNext()) {
RDFNode node = it.next();
if (node.isResource()) {
it.close();
return getId(node.as(Resource.class));
}
}
it.close();
return null;
}
};
public final Call getOneL = new Call("$rdf.getOneL") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
NodeIterator it = rdf.listObjectsOfProperty(subject, predicate);
while (it.hasNext()) {
RDFNode node = it.next();
if (node.isLiteral()) {
it.close();
return node.as(Literal.class).getValue();
}
}
it.close();
return null;
}
};
public final Call addR = new Call("$rdf.addR") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
Resource object = getResource(args[2].toString());
rdf.add(subject, predicate, object);
return null;
}
};
public final Call addL = new Call("$rdf.addL") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
// TODO better type conversion for Javascript types?
Literal object = rdf.createTypedLiteral(args[2]);
rdf.add(subject, predicate, object);
return null;
}
};
public final Call remove = new QueryCall("$rdf.remove") {
private static final long serialVersionUID = 1L;
protected Object handleQuery(Context cx, Scriptable scope,
Resource subject, Property predicate,
Resource objR, Literal objL) {
if ((objR==null) && (objL==null))
rdf.removeAll(subject, predicate, (RDFNode) null);
else if (objR != null)
rdf.removeAll(subject, predicate, objR);
else if (objL != null)
rdf.removeAll(subject, predicate, objL);
return null;
}
};
public final Call setR = new Call("$rdf.setR") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
Resource object = getResource(args[2].toString());
rdf.removeAll(subject, predicate, null);
rdf.add(subject, predicate, object);
return null;
}
};
public final Call setL = new Call("$rdf.setL") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
Resource subject = getResource(args[0].toString());
Property predicate = getPredicate(args[1].toString());
// TODO better type conversion for Javascript types?
Literal object = rdf.createTypedLiteral(args[2]);
rdf.removeAll(subject, predicate, null);
rdf.add(subject, predicate, object);
return null;
}
};
public final Call blank = new Call("$rdf.blank") {
private static final long serialVersionUID = 1L;
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {
return getId(rdf.createResource());
}
};
private Resource getResource(String id) {
if (id.startsWith("_:"))
return rdf.createResource(new AnonId(id.substring(2))); // blank
else
return rdf.createResource(id); // named
}
private Property getPredicate(String id) {
return getResource(id).as(Property.class);
}
private Scriptable asJsObject(Statement s, Context cx, Scriptable scope) {
final Scriptable triple = cx.newObject(scope);
triple.put("subject", triple, getId(s.getSubject()));
triple.put("predicate", triple, getId(s.getPredicate()));
RDFNode obj = s.getObject();
if (obj.isResource()) {
triple.put("object", triple, getId(obj.as(Resource.class)));
triple.put("type", triple, "resource");
} else {
Literal objL = obj.as(Literal.class);
// TODO nice conversion to Javascript types
triple.put("object", triple, objL.getValue());
triple.put("type", triple, "literal");
String dtype = objL.getDatatypeURI();
if (dtype != null)
triple.put("datatype", triple, dtype);
String lang = objL.getLanguage();
if ((lang != null) && !"".equals(lang))
triple.put("lang", triple, lang);
}
return triple;
}
public static String getId(Resource res) {
if (res.isURIResource())
return res.getURI(); // named
else
return "_:" + res.getId().getLabelString(); // blank
}
}