package org.deri.grefine.rdf;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.deri.grefine.rdf.ResourceNode.RdfType;
import org.deri.grefine.rdf.app.ApplicationContext;
import org.deri.grefine.rdf.vocab.PrefixExistException;
import org.deri.grefine.rdf.vocab.Vocabulary;
import org.deri.grefine.rdf.vocab.VocabularyIndexException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.refine.model.OverlayModel;
import com.google.refine.model.Project;
public class RdfSchema implements OverlayModel {
final static Logger logger = LoggerFactory.getLogger("RdfSchema");
final protected List<Node> _rootNodes = new ArrayList<Node>();
final protected List<ConstantBlankNode> _blanks = new ArrayList<ConstantBlankNode>();
protected URI baseUri;
/**
* keys are the short name, values are the full URIs e.g. foaf --> http://xmlns.com/foaf/0.1/
*/
protected Map<String,Vocabulary> prefixesMap;
public List<ConstantBlankNode> get_blanks() {
return _blanks;
}
@Override
public void onBeforeSave(Project project) {
}
@Override
public void onAfterSave(Project project) {
}
@Override
public void dispose(Project project) {
/*try {
ApplicationContext.instance().getVocabularySearcher().deleteProjectVocabularies(String.valueOf(project.id));
} catch (ParseException e) {
//log
logger.error("Unable to delete index for project " + project.id, e);
} catch (IOException e) {
//log
logger.error("Unable to delete index for project " + project.id, e);
}*/
}
public void setBaseUri(URI baseUri) {
this.baseUri = baseUri;
}
public RdfSchema(){
this.prefixesMap = new HashMap<String, Vocabulary>();
}
public RdfSchema(ApplicationContext ctxt, Project project) throws VocabularyIndexException, IOException {
// this value will never be used, just set
this.baseUri = Util.buildURI("http://localhost:3333/");
if(this.prefixesMap==null || this.prefixesMap.isEmpty()){
this.prefixesMap = clone(ctxt.getPredefinedVocabularyManager().getPredefinedVocabulariesMap());
//copy the index of predefined vocabularies
//each project will have its own copy of these predefined vocabs to enable delete, update ....
ctxt.getVocabularySearcher().addPredefinedVocabulariesToProject(project.id);
}else{
this.prefixesMap = new HashMap<String, Vocabulary>();
}
}
public void addPrefix(String name, String uri) throws PrefixExistException{
synchronized(prefixesMap){
if(this.prefixesMap.containsKey(name)){
throw new PrefixExistException(name + " already defined");
}
this.prefixesMap.put(name, new Vocabulary(name, uri));
}
}
public void removePrefix(String name){
this.prefixesMap.remove(name);
}
public URI getBaseUri() {
return baseUri;
}
public Map<String, Vocabulary> getPrefixesMap() {
return prefixesMap;
}
public void setPrefixesMap(Map<String, Vocabulary> map) {
this.prefixesMap = map;
}
public List<Node> getRoots() {
return _rootNodes;
}
static public RdfSchema reconstruct(JSONObject o) throws JSONException {
RdfSchema s = new RdfSchema();
s.baseUri = Util.buildURI(o.getString("baseUri"));
JSONArray prefixesArr;
//for backward compatibility
if(o.has("prefixes")){
prefixesArr = o.getJSONArray("prefixes");
}else{
prefixesArr = new JSONArray();
}
for (int i = 0; i < prefixesArr.length(); i++) {
JSONObject prefixObj = prefixesArr.getJSONObject(i);
String name = prefixObj.getString("name");
s.prefixesMap.put(name,new Vocabulary(name, prefixObj.getString("uri")));
}
JSONArray rootNodes = o.getJSONArray("rootNodes");
int count = rootNodes.length();
for (int i = 0; i < count; i++) {
JSONObject o2 = rootNodes.getJSONObject(i);
Node node = reconstructNode(o2, s);
if (node != null) {
s._rootNodes.add(node);
}
}
return s;
}
static protected Node reconstructNode(JSONObject o, RdfSchema s)
throws JSONException {
Node node = null;
String nodeType = o.getString("nodeType");
if (nodeType.startsWith("cell-as-")) {
boolean isRowNumberCell;
try{
isRowNumberCell = o.getBoolean("isRowNumberCell");
}catch(JSONException e){
//should never arrive here
//but for backward compatibility
isRowNumberCell = false;
}
String columnName = null;
if(!isRowNumberCell){
columnName = o.getString("columnName");
}
if ("cell-as-resource".equals(nodeType)) {
String exp = o.getString("expression");
node = new CellResourceNode(columnName, exp,isRowNumberCell);
reconstructTypes((CellResourceNode)node,o);
} else if ("cell-as-literal".equals(nodeType)) {
String valueType = o.has("valueType")?Util.getDataType(s.getBaseUri(),o.getString("valueType")):null;
String lang = o.has("lang") ? o.getString("lang"):null;
//strip off @
lang = stripAtt(lang);
String exp;
if (o.has("expression")){
exp = o.getString("expression");
}else{
//TODO backward compatibility
exp = "value";
}
node = new CellLiteralNode(columnName, exp, valueType, lang,isRowNumberCell);
} else if ("cell-as-blank".equals(nodeType)) {
//TODO blank nodes just accept value as expression
node = new CellBlankNode(columnName,"value",isRowNumberCell);
reconstructTypes((CellBlankNode)node,o);
}
} else if ("resource".equals(nodeType)) {
node = new ConstantResourceNode(o.getString("value"));
reconstructTypes((ConstantResourceNode)node,o);
} else if ("literal".equals(nodeType)) {
String valueType = o.has("valueType")?Util.getDataType(s.getBaseUri(),o.getString("valueType")):null;
String lang = o.has("lang") ? o.getString("lang"):null;
//strip off @
lang = stripAtt(lang);
node = new ConstantLiteralNode(o.getString("value"), valueType,lang);
} else if ("blank".equals(nodeType)) {
node = new ConstantBlankNode(s._blanks.size());
s._blanks.add((ConstantBlankNode) node);
reconstructTypes((ConstantBlankNode)node,o);
}
if (node != null && node instanceof ResourceNode && o.has("links")) {
ResourceNode node2 = (ResourceNode) node;
JSONArray links = o.getJSONArray("links");
int linkCount = links.length();
for (int j = 0; j < linkCount; j++) {
JSONObject oLink = links.getJSONObject(j);
node2.addLink(new Link(oLink.getString("uri"), oLink.getString("curie"),oLink
.has("target")
&& !oLink.isNull("target") ? reconstructNode(oLink
.getJSONObject("target"), s) : null));
}
}
return node;
}
static private void reconstructTypes(ResourceNode node, JSONObject o)
throws JSONException {
if (o.has("rdfTypes")) {
JSONArray arr = o.getJSONArray("rdfTypes");
List<RdfType> types = new ArrayList<RdfType>();
for (int i = 0; i < arr.length(); i++) {
String uri = arr.getJSONObject(i).getString("uri");
String curie = arr.getJSONObject(i).getString("curie");
types.add(new RdfType(uri, curie));
}
node.setTypes(types);
}
}
@Override
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("baseUri"); writer.value(baseUri);
writer.key("prefixes");
writer.array();
for(Vocabulary v:this.prefixesMap.values()){
writer.object();
writer.key("name"); writer.value(v.getName());
writer.key("uri"); writer.value(v.getUri());
writer.endObject();
}
writer.endArray();
writer.key("rootNodes");
writer.array();
for (Node node : _rootNodes) {
node.write(writer, options);
}
writer.endArray();
writer.endObject();
}
static public RdfSchema load(Project project, JSONObject obj) throws Exception {
return reconstruct(obj);
}
private Map<String,Vocabulary> clone(Map<String,Vocabulary> original){
Map<String,Vocabulary> copy = new HashMap<String, Vocabulary>();
for(Entry<String, Vocabulary> entry : original.entrySet()){
copy.put(entry.getKey(), new Vocabulary(entry.getValue().getName(),entry.getValue().getUri()));
}
return copy;
}
private static String stripAtt(String s){
if(s==null){
return s;
}
if(s.startsWith("@")){
return s.substring(1);
}
return s;
}
}