Use RDF store from Tabulator (http://www.w3.org/2005/ajar/tab) instead of Mozilla RDF support, along with a modified version of their RDF serializer. Our RDF export should now be much cleaner, but still backward compatible with 1.0.
This commit is contained in:
parent
7b58475df7
commit
7da8c036d7
10 changed files with 4266 additions and 293 deletions
6
chrome/content/zotero/xpcom/rdf.js
Normal file
6
chrome/content/zotero/xpcom/rdf.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Tweaks to get the Tabulator RDF library to work without Tabulator. All of this happens in the
|
||||
// Zotero.RDF.AJAW namespace.
|
||||
var kb = new RDFIndexedFormula();
|
||||
var tabulator = {log:{debug:function(arg) {
|
||||
Zotero.debug(arg);
|
||||
}}};
|
593
chrome/content/zotero/xpcom/rdf/identity.js
Normal file
593
chrome/content/zotero/xpcom/rdf/identity.js
Normal file
|
@ -0,0 +1,593 @@
|
|||
// Identity management and indexing for RDF
|
||||
//
|
||||
// This file provides RDFIndexedFormula a formula (set of triples) which
|
||||
// indexed by predicate, subject and object.
|
||||
//
|
||||
// It "smushes" (merges into a single node) things which are identical
|
||||
// according to owl:sameAs or an owl:InverseFunctionalProperty
|
||||
// or an owl:FunctionalProperty
|
||||
//
|
||||
//
|
||||
// 2005-10 Written Tim Berners-Lee
|
||||
// 2007 Changed so as not to munge statements from documents when smushing
|
||||
//
|
||||
//
|
||||
|
||||
/*jsl:option explicit*/ // Turn on JavaScriptLint variable declaration checking
|
||||
|
||||
owl_ns = "http://www.w3.org/2002/07/owl#";
|
||||
link_ns = "http://www.w3.org/2006/link#";
|
||||
|
||||
/* hashString functions are used as array indeces. This is done to avoid
|
||||
** conflict with existing properties of arrays such as length and map.
|
||||
** See issue 139.
|
||||
*/
|
||||
RDFLiteral.prototype.hashString = RDFLiteral.prototype.toNT;
|
||||
RDFSymbol.prototype.hashString = RDFSymbol.prototype.toNT;
|
||||
RDFBlankNode.prototype.hashString = RDFBlankNode.prototype.toNT;
|
||||
RDFCollection.prototype.hashString = RDFCollection.prototype.toNT;
|
||||
|
||||
RDFIndexedFormula.prototype = new RDFFormula();
|
||||
RDFIndexedFormula.prototype.constructor = RDFIndexedFormula;
|
||||
// RDFIndexedFormula.superclass = RDFFormula.prototype;
|
||||
RDFIndexedFormula.SuperClass = RDFFormula;
|
||||
|
||||
RDFArrayRemove = function(a, x) { //removes all elements equal to x from a
|
||||
for(var i=0; i<a.length; i++) {
|
||||
if (a[i] == x) {
|
||||
a.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw "RDFArrayRemove: Array did not contain " + x;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//Stores an associative array that maps URIs to functions
|
||||
function RDFIndexedFormula(features) {
|
||||
this.statements = []; // As in RDFFormula
|
||||
this.optional = [];
|
||||
this.propertyActions = []; // Array of functions to call when getting statement with {s X o}
|
||||
//maps <uri> to [f(F,s,p,o),...]
|
||||
this.classActions = []; // Array of functions to call when adding { s type X }
|
||||
this.redirections = []; // redirect to lexically smaller equivalent symbol
|
||||
this.aliases = []; // reverse mapping to redirection: aliases for this
|
||||
this.HTTPRedirects = []; // redirections we got from HTTP
|
||||
this.subjectIndex = []; // Array of statements with this X as subject
|
||||
this.predicateIndex = []; // Array of statements with this X as subject
|
||||
this.objectIndex = []; // Array of statements with this X as object
|
||||
this.whyIndex = []; // Array of statements with X as provenance
|
||||
this.index = [ this.subjectIndex, this.predicateIndex, this.objectIndex, this.whyIndex ];
|
||||
this.namespaces = {} // Dictionary of namespace prefixes
|
||||
if (features == undefined) features = ["sameAs",
|
||||
"InverseFunctionalProperty", "FunctionalProperty"];
|
||||
// this.features = features
|
||||
|
||||
// Callbackify?
|
||||
|
||||
function handleRDFType(formula, subj, pred, obj, why) {
|
||||
if (formula.typeCallback != undefined)
|
||||
formula.typeCallback(formula, obj, why);
|
||||
|
||||
var x = formula.classActions[obj.hashString()];
|
||||
var done = false;
|
||||
if (x) {
|
||||
for (var i=0; i<x.length; i++) {
|
||||
done = done || x[i](formula, subj, pred, obj, why);
|
||||
}
|
||||
}
|
||||
return done; // statement given is not needed if true
|
||||
} //handleRDFType
|
||||
|
||||
//If the predicate is #type, use handleRDFType to create a typeCallback on the object
|
||||
this.propertyActions[
|
||||
'<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>'] = [ handleRDFType ];
|
||||
|
||||
// Assumption: these terms are not redirected @@fixme
|
||||
if (features.indexOf("sameAs") >=0)
|
||||
this.propertyActions['<http://www.w3.org/2002/07/owl#sameAs>'] = [
|
||||
function(formula, subj, pred, obj, why) {
|
||||
formula.equate(subj,obj);
|
||||
return true; // true if statement given is NOT needed in the store
|
||||
}]; //sameAs -> equate & don't add to index
|
||||
/*
|
||||
function newPropertyAction(formula, pred, action) {
|
||||
tabulator.log.debug("newPropertyAction: "+pred);
|
||||
if (formula.propertyActions[pred] == undefined)
|
||||
formula.propertyActions[pred] = [];
|
||||
formula.propertyActions[pred].push(action);
|
||||
// Now apply the function to to statements already in the store
|
||||
var toBeFixed = formula.statementsMatching(undefined, pred, undefined);
|
||||
var i;
|
||||
for (i=0; i<toBeFixed.length; i++) { // NOT optimized - sort toBeFixed etc
|
||||
if (action(formula, toBeFixed[i].subject, pred, toBeFixed[i].object)) {
|
||||
tabulator.log.debug("newPropertyAction: NOT removing "+toBeFixed[i]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
if (features.indexOf("InverseFunctionalProperty") >= 0)
|
||||
this.classActions["<"+owl_ns+"InverseFunctionalProperty>"] = [
|
||||
function(formula, subj, pred, obj, addFn) {
|
||||
return formula.newPropertyAction(subj, handle_IFP); // yes subj not pred!
|
||||
}]; //IFP -> handle_IFP, do add to index
|
||||
|
||||
if (features.indexOf("FunctionalProperty") >= 0)
|
||||
this.classActions["<"+owl_ns+"FunctionalProperty>"] = [
|
||||
function(formula, subj, proj, obj, addFn) {
|
||||
return formula.newPropertyAction(subj, handle_FP);
|
||||
}]; //FP => handleFP, do add to index
|
||||
|
||||
function handle_IFP(formula, subj, pred, obj) {
|
||||
var s1 = formula.any(undefined, pred, obj);
|
||||
if (s1 == undefined) return false; // First time with this value
|
||||
formula.equate(s1, subj);
|
||||
return true;
|
||||
} //handle_IFP
|
||||
|
||||
function handle_FP(formula, subj, pred, obj) {
|
||||
var o1 = formula.any(subj, pred, undefined);
|
||||
if (o1 == undefined) return false; // First time with this value
|
||||
formula.equate(o1, obj);
|
||||
return true ;
|
||||
} //handle_FP
|
||||
|
||||
} /* end RDFIndexedFormula */
|
||||
|
||||
|
||||
|
||||
|
||||
RDFIndexedFormula.prototype.newPropertyAction = function newPropertyAction(pred, action) {
|
||||
tabulator.log.debug("newPropertyAction: "+pred);
|
||||
var hash = pred.hashString();
|
||||
if (this.propertyActions[hash] == undefined)
|
||||
this.propertyActions[hash] = [];
|
||||
this.propertyActions[hash].push(action);
|
||||
// Now apply the function to to statements already in the store
|
||||
var toBeFixed = this.statementsMatching(undefined, pred, undefined);
|
||||
done = false;
|
||||
for (var i=0; i<toBeFixed.length; i++) { // NOT optimized - sort toBeFixed etc
|
||||
done = done || action(this, toBeFixed[i].subject, pred, toBeFixed[i].object);
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
RDFPlainFormula = function() { return RDFIndexedFormula([]); } // No features
|
||||
|
||||
|
||||
RDFIndexedFormula.prototype.setPrefixForURI = function(prefix, nsuri) {
|
||||
//TODO:This is a hack for our own issues, which ought to be fixed post-release
|
||||
//See http://dig.csail.mit.edu/cgi-bin/roundup.cgi/tabulator/issue227
|
||||
if(prefix=="tab" && this.namespaces["tab"]) {
|
||||
return;
|
||||
}
|
||||
this.namespaces[prefix] = nsuri
|
||||
}
|
||||
|
||||
// Deprocated ... name too generic
|
||||
RDFIndexedFormula.prototype.register = function(prefix, nsuri) {
|
||||
this.namespaces[prefix] = nsuri
|
||||
}
|
||||
|
||||
|
||||
/** simplify graph in store when we realize two identifiers are equal
|
||||
|
||||
We replace the bigger with the smaller.
|
||||
|
||||
*/
|
||||
RDFIndexedFormula.prototype.equate = function(u1, u2) {
|
||||
tabulator.log.info("Equating "+u1+" and "+u2)
|
||||
|
||||
var d = u1.compareTerm(u2);
|
||||
if (!d) return true; // No information in {a = a}
|
||||
var big, small;
|
||||
if (d < 0) { // u1 less than u2
|
||||
return this.replaceWith(u2, u1);
|
||||
} else {
|
||||
return this.replaceWith(u1, u2);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace big with small, obsoleted with obsoleting.
|
||||
//
|
||||
RDFIndexedFormula.prototype.replaceWith = function(big, small) {
|
||||
tabulator.log.debug("Replacing "+big+" with "+small) // @@
|
||||
var oldhash = big.hashString();
|
||||
var newhash = small.hashString();
|
||||
|
||||
var moveIndex = function(ix) {
|
||||
var oldlist = ix[oldhash];
|
||||
if (oldlist == undefined) return; // none to move
|
||||
var newlist = ix[newhash];
|
||||
if (newlist == undefined) {
|
||||
ix[newhash] = newlist;
|
||||
} else {
|
||||
ix[newhash] = oldlist.concat(newlist);
|
||||
}
|
||||
delete ix[oldhash];
|
||||
}
|
||||
|
||||
// the canonical one carries all the indexes
|
||||
for (var i=0; i<4; i++) {
|
||||
moveIndex(this.index[i]);
|
||||
}
|
||||
|
||||
this.redirections[oldhash] = small;
|
||||
if (big.uri) {
|
||||
if (this.aliases[newhash] == undefined)
|
||||
this.aliases[newhash] = [];
|
||||
this.aliases[newhash].push(big); // Back link
|
||||
|
||||
this.add(small, this.sym('http://www.w3.org/2006/link#uri'), big.uri)
|
||||
|
||||
// If two things are equal, and one is requested, we should request the other.
|
||||
if (this.sf) {
|
||||
this.sf.nowKnownAs(big, small)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
moveIndex(this.classActions);
|
||||
moveIndex(this.propertyActions);
|
||||
|
||||
tabulator.log.debug("Equate done. "+big+" to be known as "+small)
|
||||
return true; // true means the statement does not need to be put in
|
||||
};
|
||||
|
||||
// Return the symbol with canonical URI as smushed
|
||||
RDFIndexedFormula.prototype.canon = function(term) {
|
||||
if (term == undefined) return term;
|
||||
var y = this.redirections[term.hashString()];
|
||||
if (y == undefined) return term;
|
||||
return y;
|
||||
}
|
||||
|
||||
// Compare by canonical URI as smushed
|
||||
RDFIndexedFormula.prototype.sameThings = function(x, y) {
|
||||
if (x.sameTerm(y)) return true;
|
||||
var x1 = this.canon(x);
|
||||
// alert('x1='+x1);
|
||||
if (x1 == undefined) return false;
|
||||
var y1 = this.canon(y);
|
||||
// alert('y1='+y1); //@@
|
||||
if (y1 == undefined) return false;
|
||||
return (x1.uri == y1.uri);
|
||||
}
|
||||
|
||||
// A list of all the URIs by which this thing is known
|
||||
RDFIndexedFormula.prototype.uris = function(term) {
|
||||
var cterm = this.canon(term)
|
||||
var terms = this.aliases[cterm.hashString()];
|
||||
if (!cterm.uri) return []
|
||||
var res = [ cterm.uri ]
|
||||
if (terms != undefined) {
|
||||
for (var i=0; i<terms.length; i++) {
|
||||
res.push(terms[i].uri)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// On input parameters, convert constants to terms
|
||||
//
|
||||
function RDFMakeTerm(formula,val, canonicalize) {
|
||||
if (typeof val != 'object') {
|
||||
if (typeof val == 'string')
|
||||
return new RDFLiteral(val);
|
||||
if (typeof val == 'number')
|
||||
return new RDFLiteral(val); // @@ differet types
|
||||
if (typeof val == 'boolean')
|
||||
return new RDFLiteral(val?"1":"0", undefined,
|
||||
RDFSymbol.prototype.XSDboolean);
|
||||
else if (typeof val == 'number')
|
||||
return new RDFLiteral(''+val); // @@ datatypes
|
||||
else if (typeof val == 'undefined')
|
||||
return undefined;
|
||||
else // @@ add converting of dates and numbers
|
||||
throw "Can't make Term from " + val + " of type " + typeof val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// add a triple to the store
|
||||
RDFIndexedFormula.prototype.add = function(subj, pred, obj, why) {
|
||||
var actions, st;
|
||||
if (why == undefined) why = this.fetcher ? this.fetcher.appNode: kb.sym("chrome:theSession"); //system generated
|
||||
//defined in source.js, is this OK with identity.js only user?
|
||||
subj = RDFMakeTerm(this, subj);
|
||||
pred = RDFMakeTerm(this, pred);
|
||||
obj = RDFMakeTerm(this, obj);
|
||||
why = RDFMakeTerm(this, why);
|
||||
|
||||
var hash = [ this.canon(subj).hashString(), this.canon(pred).hashString(),
|
||||
this.canon(obj).hashString(), this.canon(why).hashString()];
|
||||
|
||||
/* // Removed TimBL 2007-01-06
|
||||
// Check we don't already know it -- esp when working with dbview
|
||||
// db view has many documents with the same triple - a waste.
|
||||
// but is we want to be able to edit documents, we must maintain the original
|
||||
// triples from each one. We might occasionally want to mutiple provences too
|
||||
// for a full Truth Management System. Maybe this should be run-time option.
|
||||
st = this.anyStatementMatching(subj,pred,obj) // @@@@@@@ temp fix <====WATCH OUT!
|
||||
It is general necessary to know when data has come from >1 place.
|
||||
Maybe this should be a mode?
|
||||
*/
|
||||
// This is wasting time and shouldn't happen at all
|
||||
//st = this.anyStatementMatching(subj,pred,obj,why) // Avoid duplicates
|
||||
//if (st != undefined) return; // already in store
|
||||
|
||||
|
||||
|
||||
// tabulator.log.debug("\nActions for "+s+" "+p+" "+o+". size="+this.statements.length)
|
||||
if (this.predicateCallback != undefined)
|
||||
this.predicateCallback(this, pred, why);
|
||||
|
||||
// Action return true if the statement does not need to be added
|
||||
var actions = this.propertyActions[hash[1]]; // Predicate hash
|
||||
var done = false;
|
||||
if (actions) {
|
||||
// alert('type: '+typeof actions +' @@ actions='+actions);
|
||||
for (var i=0; i<actions.length; i++) {
|
||||
done = done || actions[i](this, subj, pred, obj, why);
|
||||
}
|
||||
}
|
||||
|
||||
//If we are tracking provenanance, every thing should be loaded into the store
|
||||
//if (done) return new RDFStatement(subj, pred, obj, why); // Don't put it in the store
|
||||
// still return this statement for owl:sameAs input
|
||||
var st = new RDFStatement(subj, pred, obj, why);
|
||||
for (var i=0; i<4; i++) {
|
||||
var ix = this.index[i];
|
||||
var h = hash[i];
|
||||
if (ix[h] == undefined) ix[h] = [];
|
||||
ix[h].push(st); // Set of things with this as subject
|
||||
}
|
||||
|
||||
tabulator.log.debug("ADDING {"+subj+" "+pred+" "+obj+"} "+why);
|
||||
this.statements.push(st);
|
||||
return st;
|
||||
}; //add
|
||||
|
||||
|
||||
// Find out whether a given URI is used as symbol in the formula
|
||||
RDFIndexedFormula.prototype.mentionsURI = function(uri) {
|
||||
var hash = '<' + uri + '>';
|
||||
return (!!this.subjectIndex[hash] || !!this.objectIndex[hash]
|
||||
|| !!this.predicateIndex[hash]);
|
||||
}
|
||||
|
||||
// Find an unused id for a file being edited: return a symbol
|
||||
// (Note: Slow iff a lot of them -- could be O(log(k)) )
|
||||
RDFIndexedFormula.prototype.nextSymbol = function(doc) {
|
||||
for(var i=0;;i++) {
|
||||
var uri = doc.uri + '#n' + i;
|
||||
if (!this.mentionsURI(uri)) return kb.sym(uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RDFIndexedFormula.prototype.anyStatementMatching = function(subj,pred,obj,why) {
|
||||
var x = this.statementsMatching(subj,pred,obj,why,true);
|
||||
if (!x || x == []) return undefined;
|
||||
return x[0];
|
||||
};
|
||||
|
||||
|
||||
// Return statements matching a pattern
|
||||
// ALL CONVENIENCE LOOKUP FUNCTIONS RELY ON THIS!
|
||||
RDFIndexedFormula.prototype.statementsMatching = function(subj,pred,obj,why,justOne) {
|
||||
tabulator.log.debug("Matching {"+subj+" "+pred+" "+obj+"}");
|
||||
|
||||
var pat = [ subj, pred, obj, why ];
|
||||
var pattern = [];
|
||||
var hash = [];
|
||||
var wild = []; // wildcards
|
||||
var given = []; // Not wild
|
||||
for (var p=0; p<4; p++) {
|
||||
pattern[p] = this.canon(RDFMakeTerm(this, pat[p]));
|
||||
if (pattern[p] == undefined) {
|
||||
wild.push(p);
|
||||
} else {
|
||||
given.push(p);
|
||||
hash[p] = pattern[p].hashString();
|
||||
}
|
||||
}
|
||||
if (given.length == 0) return this.statements; // Easy
|
||||
if (given.length == 1) { // Easy too, we have an index for that
|
||||
var p = given[0];
|
||||
var list = this.index[p][hash[p]];
|
||||
return list == undefined ? [] : list;
|
||||
}
|
||||
|
||||
// Now given.length is 2, 3 or 4.
|
||||
// We hope that the scale-free nature of the data will mean we tend to get
|
||||
// a short index in there somewhere!
|
||||
|
||||
var best = 1e10; // really bad
|
||||
var best_i;
|
||||
for (var i=0; i<given.length; i++) {
|
||||
var p = given[i]; // Which part we are dealing with
|
||||
var list = this.index[p][hash[p]];
|
||||
if (list == undefined) return []; // No occurrences
|
||||
if (list.length < best) {
|
||||
best = list.length;
|
||||
best_i = i; // (not p!)
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we have picked the shortest index but now we have to filter it
|
||||
var best_p = given[best_i];
|
||||
var possibles = this.index[best_p][hash[best_p]];
|
||||
var check = given.slice(0, best_i).concat(given.slice(best_i+1)) // remove best_i
|
||||
var results = [];
|
||||
var parts = [ 'subject', 'predicate', 'object', 'why'];
|
||||
for (var j=0; j<possibles.length; j++) {
|
||||
var st = possibles[j];
|
||||
for (var i=0; i <check.length; i++) { // for each position to be checked
|
||||
var p = check[i];
|
||||
if (!this.canon(st[parts[p]]).sameTerm(pattern[p])) {
|
||||
st = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (st != null) results.push(st);
|
||||
}
|
||||
return results;
|
||||
}; // statementsMatching
|
||||
|
||||
|
||||
/** remove a particular statement from the bank **/
|
||||
RDFIndexedFormula.prototype.remove = function (st) {
|
||||
tabulator.log.debug("entering remove w/ st=" + st);
|
||||
var term = [ st.subject, st.predicate, st.object, st.why];
|
||||
for (var p=0; p<4; p++) {
|
||||
var c = this.canon(term[p]);
|
||||
var h = c.hashString();
|
||||
if (this.index[p][h] == undefined) {
|
||||
tabulator.log.warn ("Statement removal: no index '+p+': "+st);
|
||||
} else {
|
||||
RDFArrayRemove(this.index[p][h], st);
|
||||
}
|
||||
}
|
||||
RDFArrayRemove(this.statements, st);
|
||||
}; //remove
|
||||
|
||||
/** remove all statements matching args (within limit) **/
|
||||
RDFIndexedFormula.prototype.removeMany = function (subj, pred, obj, why, limit) {
|
||||
tabulator.log.debug("entering removeMany w/ subj,pred,obj,why,limit = " + subj +", "+ pred+", " + obj+", " + why+", " + limit);
|
||||
var sts = this.statementsMatching (subj, pred, obj, why, false);
|
||||
//This is a subtle bug that occcured in updateCenter.js too.
|
||||
//The fact is, this.statementsMatching returns this.whyIndex instead of a copy of it
|
||||
//but for perfromance consideration, it's better to just do that
|
||||
//so make a copy here.
|
||||
var statements = [];
|
||||
for (var i=0;i<sts.length;i++) statements.push(sts[i]);
|
||||
if (limit) statements = statements.slice(0, limit);
|
||||
for (var st in statements) this.remove(statements[st]);
|
||||
}; //removeMany
|
||||
|
||||
/** Load a resorce into the store **/
|
||||
|
||||
RDFIndexedFormula.prototype.load = function(url) {
|
||||
// get the XML
|
||||
var xhr = Util.XMLHTTPFactory(); // returns a new XMLHttpRequest, or ActiveX XMLHTTP object
|
||||
if (xhr.overrideMimeType) {
|
||||
xhr.overrideMimeType("text/xml");
|
||||
}
|
||||
|
||||
// Get privileges for cross-domain web access
|
||||
if(!isExtension) {
|
||||
try {
|
||||
Util.enablePrivilege("UniversalXPConnect UniversalBrowserRead")
|
||||
} catch(e) {
|
||||
throw ("Failed to get privileges: (see http://dig.csail.mit.edu/2005/ajar/ajaw/Privileges.html)" + e)
|
||||
}
|
||||
}
|
||||
|
||||
xhr.open("GET", url, false); // Synchronous
|
||||
xhr.send("");
|
||||
|
||||
// Get XML DOM Tree
|
||||
|
||||
var nodeTree = xhr.responseXML;
|
||||
if (nodeTree === null && xhr.responseText !== null) {
|
||||
// Only if the server fails to set Content-Type: text/xml AND xmlhttprequest doesn't have the overrideMimeType method
|
||||
nodeTree = (new DOMParser()).parseFromString(xhr.responseText, 'text/xml');
|
||||
}
|
||||
|
||||
// Get RDF statements fromm XML
|
||||
|
||||
// must be an XML document node tree
|
||||
var parser = new RDFParser(this);
|
||||
parser.parse(nodeTree,url);
|
||||
}
|
||||
|
||||
|
||||
/** Utility**/
|
||||
|
||||
/* @method: copyTo
|
||||
@discription: replace @template with @target and add appropriate triples (no triple removed)
|
||||
one-direction replication
|
||||
*/
|
||||
RDFIndexedFormula.prototype.copyTo = function(template,target,flags){
|
||||
if (!flags) flags=[];
|
||||
var statList=this.statementsMatching(template);
|
||||
if (flags.indexOf('two-direction')!=-1)
|
||||
statList.concat(this.statementsMatching(undefined,undefined,template));
|
||||
for (var i=0;i<statList.length;i++){
|
||||
var st=statList[i];
|
||||
switch (st.object.termType){
|
||||
case 'symbol':
|
||||
this.add(target,st.predicate,st.object);
|
||||
break;
|
||||
case 'literal':
|
||||
case 'bnode':
|
||||
case 'collection':
|
||||
this.add(target,st.predicate,st.object.copy(this));
|
||||
}
|
||||
if (flags.indexOf('delete')!=-1) this.remove(st);
|
||||
}
|
||||
};
|
||||
//for the case when you alter this.value (text modified in userinput.js)
|
||||
RDFLiteral.prototype.copy = function(){
|
||||
return new RDFLiteral(this.value,this.lang,this.datatype);
|
||||
};
|
||||
RDFBlankNode.prototype.copy = function(formula){ //depends on the formula
|
||||
var bnodeNew=new RDFBlankNode();
|
||||
formula.copyTo(this,bnodeNew);
|
||||
return bnodeNew;
|
||||
}
|
||||
/** Full N3 bits -- placeholders only to allow parsing, no functionality! **/
|
||||
|
||||
RDFIndexedFormula.prototype.newUniversal = function(uri) {
|
||||
var x = this.sym(uri);
|
||||
if (!this._universalVariables) this._universalVariables = [];
|
||||
this._universalVariables.push(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
RDFIndexedFormula.prototype.newExistential = function(uri) {
|
||||
if (!uri) return this.bnode();
|
||||
var x = this.sym(uri);
|
||||
return this.declareExistential(x);
|
||||
}
|
||||
|
||||
RDFIndexedFormula.prototype.declareExistential = function(x) {
|
||||
if (!this._existentialVariables) this._existentialVariables = [];
|
||||
this._existentialVariables.push(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
RDFIndexedFormula.prototype.formula = function(features) {
|
||||
return new RDFIndexedFormula(features);
|
||||
}
|
||||
|
||||
RDFIndexedFormula.prototype.close = function() {
|
||||
return this;
|
||||
}
|
||||
|
||||
RDFIndexedFormula.prototype.hashString = RDFIndexedFormula.prototype.toNT;
|
||||
|
||||
///////////////////////////// Provenance tracking
|
||||
//
|
||||
// Where did this statement come from?
|
||||
//
|
||||
|
||||
/*
|
||||
RDFStatement.prototype.original = function() {
|
||||
for (var st = this;; st = st.why.premis[0]) {
|
||||
if (st.why.termType && st.why.termType== 'symbol')
|
||||
return this; // This statement came from a document
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// ends
|
||||
|
178
chrome/content/zotero/xpcom/rdf/match.js
Normal file
178
chrome/content/zotero/xpcom/rdf/match.js
Normal file
|
@ -0,0 +1,178 @@
|
|||
// Matching a statement against a formula
|
||||
//
|
||||
//
|
||||
// W3C open source licence 2005.
|
||||
//
|
||||
// We retpresent a set as an associative array whose value for
|
||||
// each member is set to true.
|
||||
|
||||
/* Not used, bogus. See identity.js for the ones really used.
|
||||
RDFFormula.prototype.statementsMatching = function(s,p,o,w) {
|
||||
var results = []
|
||||
var i
|
||||
var ls = this.statements.length
|
||||
for (i=0; i<ls; i++) {
|
||||
var st = this.statements[i]
|
||||
if (RDFTermMatch(p, st.predicate) && // first as simplest
|
||||
RDFTermMatch(s, st.subject) &&
|
||||
RDFTermMatch(o, st.object) &&
|
||||
RDFTermMatch(w, st.why)) {
|
||||
results[st] = true @@@@ sould use numeric indexed array
|
||||
}
|
||||
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
RDFFormula.prototype.anyStatementMatching = function(s,p,o,w) {
|
||||
var ls = this.statements.length
|
||||
var i
|
||||
for (i=0; i<ls; i++) {
|
||||
var st = this.statements[i]
|
||||
if (RDFTermMatch(p, st.predicate) && // first as simplest
|
||||
RDFTermMatch(s, st.subject) &&
|
||||
RDFTermMatch(o, st.object) &&
|
||||
RDFTermMatch(w, st.why)) {
|
||||
return st
|
||||
}
|
||||
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function RDFTermMatch(pattern, term) {
|
||||
if (typeof pattern == 'undefined') return true;
|
||||
return pattern.sameTerm(term)
|
||||
}
|
||||
|
||||
RDFSymbol.prototype.sameTerm = function(other) {
|
||||
if (!other) { return false }
|
||||
return ((this.termType == other.termType) && (this.uri == other.uri))
|
||||
}
|
||||
|
||||
RDFBlankNode.prototype.sameTerm = function(other) {
|
||||
if (!other) { return false }
|
||||
return ((this.termType == other.termType) && (this.id == other.id))
|
||||
}
|
||||
|
||||
RDFLiteral.prototype.sameTerm = function(other) {
|
||||
if (!other) { return false }
|
||||
return ((this.termType == other.termType)
|
||||
&& (this.value == other.value)
|
||||
&& (this.lang == other.lang) &&
|
||||
((!this.datatype && !other.datatype)
|
||||
|| this.datatype.sameTerm(other.datatype)))
|
||||
}
|
||||
|
||||
RDFVariable.prototype.sameTerm = function (other) {
|
||||
if (!other) { return false }
|
||||
return((this.termType == other.termType) && (this.uri == other.uri))
|
||||
}
|
||||
|
||||
RDFCollection.prototype.sameTerm = RDFBlankNode.prototype.sameTerm
|
||||
|
||||
RDFFormula.prototype.sameTerm = function (other) {
|
||||
return this.hashString() == other.hashString();
|
||||
}
|
||||
// Comparison for ordering
|
||||
//
|
||||
// These compare with ANY term
|
||||
//
|
||||
//
|
||||
// When we smush nodes we take the lowest value. This is not
|
||||
// arbitrary: we want the value actually used to be the literal
|
||||
// (or list or formula).
|
||||
|
||||
RDFLiteral.prototype.classOrder = 1
|
||||
// RDFList.prototype.classOrder = 2
|
||||
// RDFSet.prototype.classOrder = 3
|
||||
RDFCollection.prototype.classOrder = 3
|
||||
RDFFormula.prototype.classOrder = 4
|
||||
RDFSymbol.prototype.classOrder = 5
|
||||
RDFBlankNode.prototype.classOrder = 6
|
||||
|
||||
// Compaisons return sign(self - other)
|
||||
// Literals must come out before terms for smushing
|
||||
|
||||
RDFLiteral.prototype.compareTerm = function(other) {
|
||||
if (this.classOrder < other.classOrder) return -1
|
||||
if (this.classOrder > other.classOrder) return +1
|
||||
if (this.value < other.value) return -1
|
||||
if (this.value > other.value) return +1
|
||||
return 0
|
||||
}
|
||||
|
||||
RDFSymbol.prototype.compareTerm = function(other) {
|
||||
if (this.classOrder < other.classOrder) return -1
|
||||
if (this.classOrder > other.classOrder) return +1
|
||||
if (this.uri < other.uri) return -1
|
||||
if (this.uri > other.uri) return +1
|
||||
return 0
|
||||
}
|
||||
|
||||
RDFBlankNode.prototype.compareTerm = function(other) {
|
||||
if (this.classOrder < other.classOrder) return -1
|
||||
if (this.classOrder > other.classOrder) return +1
|
||||
if (this.id < other.id) return -1
|
||||
if (this.id > other.id) return +1
|
||||
return 0
|
||||
}
|
||||
|
||||
RDFCollection.prototype.compareTerm = RDFBlankNode.prototype.compareTerm
|
||||
|
||||
// Convenience routines
|
||||
|
||||
// Only one of s p o can be undefined, and w is optional.
|
||||
RDFFormula.prototype.each = function(s,p,o,w) {
|
||||
var results = []
|
||||
var st, sts = this.statementsMatching(s,p,o,w)
|
||||
var i, n=sts.length
|
||||
if (typeof s == 'undefined') {
|
||||
for (i=0; i<n; i++) {st=sts[i]; results.push(st.subject)}
|
||||
} else if (typeof p == 'undefined') {
|
||||
for (i=0; i<n; i++) {st=sts[i]; results.push(st.predicate)}
|
||||
} else if (typeof o == 'undefined') {
|
||||
for (i=0; i<n; i++) {st=sts[i]; results.push(st.object)}
|
||||
} else if (typeof w == 'undefined') {
|
||||
for (i=0; i<n; i++) {st=sts[i]; results.push(st.why)}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
RDFFormula.prototype.any = function(s,p,o,w) {
|
||||
var st = this.anyStatementMatching(s,p,o,w)
|
||||
if (typeof st == 'undefined') return undefined;
|
||||
|
||||
if (typeof s == 'undefined') return st.subject;
|
||||
if (typeof p == 'undefined') return st.predicate;
|
||||
if (typeof o == 'undefined') return st.object;
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
RDFFormula.prototype.the = function(s,p,o,w) {
|
||||
// the() should contain a check there is only one
|
||||
var x = this.any(s,p,o,w)
|
||||
if (typeof x == 'undefined')
|
||||
tabulator.log.error("No value found for the(){" + s + " " + p + " " + o + "}.")
|
||||
return x
|
||||
}
|
||||
|
||||
RDFFormula.prototype.whether = function(s,p,o,w) {
|
||||
return this.statementsMatching(s,p,o,w).length;
|
||||
}
|
||||
|
||||
// Not a method. For use in sorts
|
||||
function RDFComparePredicateObject(self, other) {
|
||||
var x = self.predicate.compareTerm(other.predicate)
|
||||
if (x !=0) return x
|
||||
return self.object.compareTerm(other.object)
|
||||
}
|
||||
function RDFComparePredicateSubject(self, other) {
|
||||
var x = self.predicate.compareTerm(other.predicate)
|
||||
if (x !=0) return x
|
||||
return self.subject.compareTerm(other.subject)
|
||||
}
|
||||
// ends
|
1526
chrome/content/zotero/xpcom/rdf/n3parser.js
Normal file
1526
chrome/content/zotero/xpcom/rdf/n3parser.js
Normal file
File diff suppressed because it is too large
Load diff
562
chrome/content/zotero/xpcom/rdf/rdfparser.js
Normal file
562
chrome/content/zotero/xpcom/rdf/rdfparser.js
Normal file
|
@ -0,0 +1,562 @@
|
|||
/**
|
||||
* @fileoverview
|
||||
* TABULATOR RDF PARSER
|
||||
*
|
||||
* Version 0.1
|
||||
* Parser believed to be in full positive RDF/XML parsing compliance
|
||||
* with the possible exception of handling deprecated RDF attributes
|
||||
* appropriately. Parser is believed to comply fully with other W3C
|
||||
* and industry standards where appropriate (DOM, ECMAScript, &c.)
|
||||
*
|
||||
* Author: David Sheets <dsheets@mit.edu>
|
||||
* SVN ID: $Id$
|
||||
*
|
||||
* W3C® SOFTWARE NOTICE AND LICENSE
|
||||
* http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
* This work (and included software, documentation such as READMEs, or
|
||||
* other related items) is being provided by the copyright holders under
|
||||
* the following license. By obtaining, using and/or copying this work,
|
||||
* you (the licensee) agree that you have read, understood, and will
|
||||
* comply with the following terms and conditions.
|
||||
*
|
||||
* Permission to copy, modify, and distribute this software and its
|
||||
* documentation, with or without modification, for any purpose and
|
||||
* without fee or royalty is hereby granted, provided that you include
|
||||
* the following on ALL copies of the software and documentation or
|
||||
* portions thereof, including modifications:
|
||||
*
|
||||
* 1. The full text of this NOTICE in a location viewable to users of
|
||||
* the redistributed or derivative work.
|
||||
* 2. Any pre-existing intellectual property disclaimers, notices, or terms and
|
||||
* conditions. If none exist, the W3C Software Short Notice should be
|
||||
* included (hypertext is preferred, text is permitted) within the body
|
||||
* of any redistributed or derivative code.
|
||||
* 3. Notice of any changes or modifications to the files, including the
|
||||
* date changes were made. (We recommend you provide URIs to the location
|
||||
* from which the code is derived.)
|
||||
*
|
||||
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT
|
||||
* HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS
|
||||
* FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR
|
||||
* DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
|
||||
* TRADEMARKS OR OTHER RIGHTS.
|
||||
*
|
||||
* COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL
|
||||
* OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders.
|
||||
*/
|
||||
/**
|
||||
* @class Class defining an RDFParser resource object tied to an RDFStore
|
||||
*
|
||||
* @author David Sheets <dsheets@mit.edu>
|
||||
* @version 0.1
|
||||
*
|
||||
* @constructor
|
||||
* @param {RDFStore} store An RDFStore object
|
||||
*/
|
||||
function RDFParser(store) {
|
||||
/** Standard namespaces that we know how to handle @final
|
||||
* @member RDFParser
|
||||
*/
|
||||
RDFParser['ns'] = {'RDF':
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||
'RDFS':
|
||||
"http://www.w3.org/2000/01/rdf-schema#"}
|
||||
/** DOM Level 2 node type magic numbers @final
|
||||
* @member RDFParser
|
||||
*/
|
||||
RDFParser['nodeType'] = {'ELEMENT': 1, 'ATTRIBUTE': 2, 'TEXT': 3,
|
||||
'CDATA_SECTION': 4, 'ENTITY_REFERENCE': 5,
|
||||
'ENTITY': 6, 'PROCESSING_INSTRUCTION': 7,
|
||||
'COMMENT': 8, 'DOCUMENT': 9, 'DOCUMENT_TYPE': 10,
|
||||
'DOCUMENT_FRAGMENT': 11, 'NOTATION': 12}
|
||||
|
||||
/**
|
||||
* Frame class for namespace and base URI lookups
|
||||
* Base lookups will always resolve because the parser knows
|
||||
* the default base.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
this['frameFactory'] = function (parser, parent, element) {
|
||||
return {'NODE': 1,
|
||||
'ARC': 2,
|
||||
'parent': parent,
|
||||
'parser': parser,
|
||||
'store': parser['store'],
|
||||
'element': element,
|
||||
'lastChild': 0,
|
||||
'base': null,
|
||||
'lang': null,
|
||||
'node': null,
|
||||
'nodeType': null,
|
||||
'listIndex': 1,
|
||||
'rdfid': null,
|
||||
'datatype': null,
|
||||
'collection': false,
|
||||
|
||||
/** Terminate the frame and notify the store that we're done */
|
||||
'terminateFrame': function () {
|
||||
if (this['collection']) {
|
||||
this['node']['close']()
|
||||
}
|
||||
},
|
||||
|
||||
/** Add a symbol of a certain type to the this frame */
|
||||
'addSymbol': function (type, uri) {
|
||||
uri = Util.uri.join(uri, this['base'])
|
||||
this['node'] = this['store']['sym'](uri)
|
||||
this['nodeType'] = type
|
||||
},
|
||||
|
||||
/** Load any constructed triples into the store */
|
||||
'loadTriple': function () {
|
||||
if (this['parent']['parent']['collection']) {
|
||||
this['parent']['parent']['node']['append'](this['node'])
|
||||
}
|
||||
else {
|
||||
this['store']['add'](this['parent']['parent']['node'],
|
||||
this['parent']['node'],
|
||||
this['node'],
|
||||
this['parser']['why'])
|
||||
}
|
||||
if (this['parent']['rdfid'] != null) { // reify
|
||||
var triple = this['store']['sym'](
|
||||
Util.uri.join("#"+this['parent']['rdfid'],
|
||||
this['base']))
|
||||
this['store']['add'](triple,
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']
|
||||
+"type"),
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']
|
||||
+"Statement"),
|
||||
this['parser']['why'])
|
||||
this['store']['add'](triple,
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']
|
||||
+"subject"),
|
||||
this['parent']['parent']['node'],
|
||||
this['parser']['why'])
|
||||
this['store']['add'](triple,
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']
|
||||
+"predicate"),
|
||||
this['parent']['node'],
|
||||
this['parser']['why'])
|
||||
this['store']['add'](triple,
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']
|
||||
+"object"),
|
||||
this['node'],
|
||||
this['parser']['why'])
|
||||
}
|
||||
},
|
||||
|
||||
/** Check if it's OK to load a triple */
|
||||
'isTripleToLoad': function () {
|
||||
return (this['parent'] != null
|
||||
&& this['parent']['parent'] != null
|
||||
&& this['nodeType'] == this['NODE']
|
||||
&& this['parent']['nodeType'] == this['ARC']
|
||||
&& this['parent']['parent']['nodeType']
|
||||
== this['NODE'])
|
||||
},
|
||||
|
||||
/** Add a symbolic node to this frame */
|
||||
'addNode': function (uri) {
|
||||
this['addSymbol'](this['NODE'],uri)
|
||||
if (this['isTripleToLoad']()) {
|
||||
this['loadTriple']()
|
||||
}
|
||||
},
|
||||
|
||||
/** Add a collection node to this frame */
|
||||
'addCollection': function () {
|
||||
this['nodeType'] = this['NODE']
|
||||
this['node'] = this['store']['collection']()
|
||||
this['collection'] = true
|
||||
if (this['isTripleToLoad']()) {
|
||||
this['loadTriple']()
|
||||
}
|
||||
},
|
||||
|
||||
/** Add a collection arc to this frame */
|
||||
'addCollectionArc': function () {
|
||||
this['nodeType'] = this['ARC']
|
||||
},
|
||||
|
||||
/** Add a bnode to this frame */
|
||||
'addBNode': function (id) {
|
||||
if (id != null) {
|
||||
if (this['parser']['bnodes'][id] != null) {
|
||||
this['node'] = this['parser']['bnodes'][id]
|
||||
} else {
|
||||
this['node'] = this['parser']['bnodes'][id] = this['store']['bnode']()
|
||||
}
|
||||
} else { this['node'] = this['store']['bnode']() }
|
||||
|
||||
this['nodeType'] = this['NODE']
|
||||
if (this['isTripleToLoad']()) {
|
||||
this['loadTriple']()
|
||||
}
|
||||
},
|
||||
|
||||
/** Add an arc or property to this frame */
|
||||
'addArc': function (uri) {
|
||||
if (uri == RDFParser['ns']['RDF']+"li") {
|
||||
uri = RDFParser['ns']['RDF']+"_"+this['parent']['listIndex']++
|
||||
}
|
||||
this['addSymbol'](this['ARC'], uri)
|
||||
},
|
||||
|
||||
/** Add a literal to this frame */
|
||||
'addLiteral': function (value) {
|
||||
if (this['parent']['datatype']) {
|
||||
this['node'] = this['store']['literal'](
|
||||
value, "", this['store']['sym'](
|
||||
this['parent']['datatype']))
|
||||
}
|
||||
else {
|
||||
this['node'] = this['store']['literal'](
|
||||
value, this['lang'])
|
||||
}
|
||||
this['nodeType'] = this['NODE']
|
||||
if (this['isTripleToLoad']()) {
|
||||
this['loadTriple']()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Our triple store reference @private */
|
||||
this['store'] = store
|
||||
/** Our identified blank nodes @private */
|
||||
this['bnodes'] = {}
|
||||
/** A context for context-aware stores @private */
|
||||
this['why'] = null
|
||||
/** Reification flag */
|
||||
this['reify'] = false
|
||||
|
||||
/**
|
||||
* Build our initial scope frame and parse the DOM into triples
|
||||
* @param {DOMTree} document The DOM to parse
|
||||
* @param {String} base The base URL to use
|
||||
* @param {Object} why The context to which this resource belongs
|
||||
*/
|
||||
this['parse'] = function (document, base, why) {
|
||||
// alert('parse base:'+base);
|
||||
var children = document['childNodes']
|
||||
|
||||
// clean up for the next run
|
||||
this['cleanParser']()
|
||||
|
||||
// figure out the root element
|
||||
var root = document.documentElement; //this is faster, I think, cross-browser issue? well, DOM 2
|
||||
/*
|
||||
if (document['nodeType'] == RDFParser['nodeType']['DOCUMENT']) {
|
||||
for (var c=0; c<children['length']; c++) {
|
||||
if (children[c]['nodeType']
|
||||
== RDFParser['nodeType']['ELEMENT']) {
|
||||
var root = children[c]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (document['nodeType'] == RDFParser['nodeType']['ELEMENT']) {
|
||||
var root = document
|
||||
}
|
||||
else {
|
||||
throw new Error("RDFParser: can't find root in " + base
|
||||
+ ". Halting. ")
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
this['why'] = why
|
||||
|
||||
|
||||
// our topmost frame
|
||||
|
||||
var f = this['frameFactory'](this)
|
||||
this['base'] = base
|
||||
f['base'] = base
|
||||
f['lang'] = ''
|
||||
|
||||
this['parseDOM'](this['buildFrame'](f,root))
|
||||
return true
|
||||
}
|
||||
this['parseDOM'] = function (frame) {
|
||||
// a DOM utility function used in parsing
|
||||
var elementURI = function (el) {
|
||||
if (el['namespaceURI'] == null) {
|
||||
throw new Error("RDF/XML syntax error: No namespace for "
|
||||
+el['localName']+" in "+this.base)
|
||||
}
|
||||
return el['namespaceURI'] + el['localName']
|
||||
}
|
||||
var dig = true // if we'll dig down in the tree on the next iter
|
||||
|
||||
while (frame['parent']) {
|
||||
var dom = frame['element']
|
||||
var attrs = dom['attributes']
|
||||
|
||||
if (dom['nodeType']
|
||||
== RDFParser['nodeType']['TEXT']
|
||||
|| dom['nodeType']
|
||||
== RDFParser['nodeType']['CDATA_SECTION']) {//we have a literal
|
||||
frame['addLiteral'](dom['nodeValue'])
|
||||
}
|
||||
else if (elementURI(dom)
|
||||
!= RDFParser['ns']['RDF']+"RDF") { // not root
|
||||
if (frame['parent'] && frame['parent']['collection']) {
|
||||
// we're a collection element
|
||||
frame['addCollectionArc']()
|
||||
frame = this['buildFrame'](frame,frame['element'])
|
||||
frame['parent']['element'] = null
|
||||
}
|
||||
if (!frame['parent'] || !frame['parent']['nodeType']
|
||||
|| frame['parent']['nodeType'] == frame['ARC']) {
|
||||
// we need a node
|
||||
var about =dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"about")
|
||||
var rdfid =dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"ID")
|
||||
if (about && rdfid) {
|
||||
throw new Error("RDFParser: " + dom['nodeName']
|
||||
+ " has both rdf:id and rdf:about."
|
||||
+ " Halting. Only one of these"
|
||||
+ " properties may be specified on a"
|
||||
+ " node.");
|
||||
}
|
||||
if (about == null && rdfid) {
|
||||
frame['addNode']("#"+rdfid['nodeValue'])
|
||||
dom['removeAttributeNode'](rdfid)
|
||||
}
|
||||
else if (about == null && rdfid == null) {
|
||||
var bnid = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"nodeID")
|
||||
if (bnid) {
|
||||
frame['addBNode'](bnid['nodeValue'])
|
||||
dom['removeAttributeNode'](bnid)
|
||||
} else { frame['addBNode']() }
|
||||
}
|
||||
else {
|
||||
frame['addNode'](about['nodeValue'])
|
||||
dom['removeAttributeNode'](about)
|
||||
}
|
||||
|
||||
// Typed nodes
|
||||
var rdftype = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"type")
|
||||
if (RDFParser['ns']['RDF']+"Description"
|
||||
!= elementURI(dom)) {
|
||||
rdftype = {'nodeValue': elementURI(dom)}
|
||||
}
|
||||
if (rdftype != null) {
|
||||
this['store']['add'](frame['node'],
|
||||
this['store']['sym'](
|
||||
RDFParser['ns']['RDF']+"type"),
|
||||
this['store']['sym'](
|
||||
Util.uri.join(
|
||||
rdftype['nodeValue'],
|
||||
frame['base'])),
|
||||
this['why'])
|
||||
if (rdftype['nodeName']){
|
||||
dom['removeAttributeNode'](rdftype)
|
||||
}
|
||||
}
|
||||
|
||||
// Property Attributes
|
||||
for (var x = attrs['length']-1; x >= 0; x--) {
|
||||
this['store']['add'](frame['node'],
|
||||
this['store']['sym'](
|
||||
elementURI(attrs[x])),
|
||||
this['store']['literal'](
|
||||
attrs[x]['nodeValue'],
|
||||
frame['lang']),
|
||||
this['why'])
|
||||
}
|
||||
}
|
||||
else { // we should add an arc (or implicit bnode+arc)
|
||||
frame['addArc'](elementURI(dom))
|
||||
|
||||
// save the arc's rdf:ID if it has one
|
||||
if (this['reify']) {
|
||||
var rdfid = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"ID")
|
||||
if (rdfid) {
|
||||
frame['rdfid'] = rdfid['nodeValue']
|
||||
dom['removeAttributeNode'](rdfid)
|
||||
}
|
||||
}
|
||||
|
||||
var parsetype = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"parseType")
|
||||
var datatype = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"datatype")
|
||||
if (datatype) {
|
||||
frame['datatype'] = datatype['nodeValue']
|
||||
dom['removeAttributeNode'](datatype)
|
||||
}
|
||||
|
||||
if (parsetype) {
|
||||
var nv = parsetype['nodeValue']
|
||||
if (nv == "Literal") {
|
||||
frame['datatype']
|
||||
= RDFParser['ns']['RDF']+"XMLLiteral"
|
||||
// (this.buildFrame(frame)).addLiteral(dom)
|
||||
// should work but doesn't
|
||||
frame = this['buildFrame'](frame)
|
||||
frame['addLiteral'](dom)
|
||||
dig = false
|
||||
}
|
||||
else if (nv == "Resource") {
|
||||
frame = this['buildFrame'](frame,frame['element'])
|
||||
frame['parent']['element'] = null
|
||||
frame['addBNode']()
|
||||
}
|
||||
else if (nv == "Collection") {
|
||||
frame = this['buildFrame'](frame,frame['element'])
|
||||
frame['parent']['element'] = null
|
||||
frame['addCollection']()
|
||||
}
|
||||
dom['removeAttributeNode'](parsetype)
|
||||
}
|
||||
|
||||
if (attrs['length'] != 0) {
|
||||
var resource = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"resource")
|
||||
var bnid = dom['getAttributeNodeNS'](
|
||||
RDFParser['ns']['RDF'],"nodeID")
|
||||
|
||||
frame = this['buildFrame'](frame)
|
||||
if (resource) {
|
||||
frame['addNode'](resource['nodeValue'])
|
||||
dom['removeAttributeNode'](resource)
|
||||
} else {
|
||||
if (bnid) {
|
||||
frame['addBNode'](bnid['nodeValue'])
|
||||
dom['removeAttributeNode'](bnid)
|
||||
} else { frame['addBNode']() }
|
||||
}
|
||||
|
||||
for (var x = attrs['length']-1; x >= 0; x--) {
|
||||
var f = this['buildFrame'](frame)
|
||||
f['addArc'](elementURI(attrs[x]))
|
||||
if (elementURI(attrs[x])
|
||||
==RDFParser['ns']['RDF']+"type"){
|
||||
(this['buildFrame'](f))['addNode'](
|
||||
attrs[x]['nodeValue'])
|
||||
} else {
|
||||
(this['buildFrame'](f))['addLiteral'](
|
||||
attrs[x]['nodeValue'])
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dom['childNodes']['length'] == 0) {
|
||||
(this['buildFrame'](frame))['addLiteral']("")
|
||||
}
|
||||
}
|
||||
} // rdf:RDF
|
||||
|
||||
// dig dug
|
||||
dom = frame['element']
|
||||
while (frame['parent']) {
|
||||
var pframe = frame
|
||||
while (dom == null) {
|
||||
frame = frame['parent']
|
||||
dom = frame['element']
|
||||
}
|
||||
var candidate = dom['childNodes'][frame['lastChild']]
|
||||
if (candidate == null || !dig) {
|
||||
frame['terminateFrame']()
|
||||
if (!(frame = frame['parent'])) { break } // done
|
||||
dom = frame['element']
|
||||
dig = true
|
||||
}
|
||||
else if ((candidate['nodeType']
|
||||
!= RDFParser['nodeType']['ELEMENT']
|
||||
&& candidate['nodeType']
|
||||
!= RDFParser['nodeType']['TEXT']
|
||||
&& candidate['nodeType']
|
||||
!= RDFParser['nodeType']['CDATA_SECTION'])
|
||||
|| ((candidate['nodeType']
|
||||
== RDFParser['nodeType']['TEXT']
|
||||
|| candidate['nodeType']
|
||||
== RDFParser['nodeType']['CDATA_SECTION'])
|
||||
&& dom['childNodes']['length'] != 1)) {
|
||||
frame['lastChild']++
|
||||
}
|
||||
else { // not a leaf
|
||||
frame['lastChild']++
|
||||
frame = this['buildFrame'](pframe,
|
||||
dom['childNodes'][frame['lastChild']-1])
|
||||
break
|
||||
}
|
||||
}
|
||||
} // while
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out state from a previous parse run
|
||||
* @private
|
||||
*/
|
||||
this['cleanParser'] = function () {
|
||||
this['bnodes'] = {}
|
||||
this['why'] = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds scope frame
|
||||
* @private
|
||||
*/
|
||||
this['buildFrame'] = function (parent, element) {
|
||||
var frame = this['frameFactory'](this,parent,element)
|
||||
if (parent) {
|
||||
frame['base'] = parent['base']
|
||||
frame['lang'] = parent['lang']
|
||||
}
|
||||
if (element == null
|
||||
|| element['nodeType'] == RDFParser['nodeType']['TEXT']
|
||||
|| element['nodeType'] == RDFParser['nodeType']['CDATA_SECTION']) {
|
||||
return frame
|
||||
}
|
||||
|
||||
var attrs = element['attributes']
|
||||
|
||||
var base = element['getAttributeNode']("xml:base")
|
||||
if (base != null) {
|
||||
frame['base'] = base['nodeValue']
|
||||
element['removeAttribute']("xml:base")
|
||||
}
|
||||
var lang = element['getAttributeNode']("xml:lang")
|
||||
if (lang != null) {
|
||||
frame['lang'] = lang['nodeValue']
|
||||
element['removeAttribute']("xml:lang")
|
||||
}
|
||||
|
||||
// remove all extraneous xml and xmlns attributes
|
||||
for (var x = attrs['length']-1; x >= 0; x--) {
|
||||
if (attrs[x]['nodeName']['substr'](0,3) == "xml") {
|
||||
if (attrs[x].name.slice(0,6)=='xmlns:') {
|
||||
var uri = attrs[x].nodeValue;
|
||||
// alert('base for namespac attr:'+this.base);
|
||||
if (this.base) uri = Util.uri.join(uri, this.base);
|
||||
this.store.setPrefixForURI(attrs[x].name.slice(6),
|
||||
uri);
|
||||
}
|
||||
// alert('rdfparser: xml atribute: '+attrs[x].name) //@@
|
||||
element['removeAttributeNode'](attrs[x])
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}
|
||||
}
|
700
chrome/content/zotero/xpcom/rdf/serialize.js
Normal file
700
chrome/content/zotero/xpcom/rdf/serialize.js
Normal file
|
@ -0,0 +1,700 @@
|
|||
/* Serialization of RDF Graphs
|
||||
**
|
||||
** Tim Berners-Lee 2006
|
||||
** This is or was http://dig.csail.mit.edu/2005/ajar/ajaw/js/rdf/serialize.js
|
||||
**
|
||||
** Bug: can't serialize http://data.semanticweb.org/person/abraham-bernstein/rdf
|
||||
** in XML (from mhausenblas)
|
||||
*/
|
||||
|
||||
__Serializer = function(){
|
||||
this.flags = "";
|
||||
this.base = null;
|
||||
this.prefixes = [];
|
||||
this.keywords = ['a']; // The only one we generate at the moment
|
||||
this.prefixchars = "abcdefghijklmnopqustuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
this.incoming = null; // Array not calculated yet
|
||||
this.formulas = []; // remebering original formulae from hashes
|
||||
|
||||
/* pass */
|
||||
}
|
||||
|
||||
Serializer = function() {return new __Serializer()};
|
||||
|
||||
__Serializer.prototype.setBase = function(base)
|
||||
{ this.base = base };
|
||||
|
||||
__Serializer.prototype.setFlags = function(flags)
|
||||
{ this.flags = flags?flags: '' };
|
||||
|
||||
|
||||
__Serializer.prototype.toStr = function(x) {
|
||||
var s = x.toNT();
|
||||
if (x.termType == 'formula') {
|
||||
this.formulas[s] = x; // remember as reverse does not work
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
__Serializer.prototype.fromStr = function(s) {
|
||||
if (s[0] == '{') {
|
||||
var x = this.formulas[s];
|
||||
if (!x) alert('No formula object for '+s)
|
||||
return x;
|
||||
}
|
||||
return kb.fromNT(s);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Accumulate Namespaces
|
||||
**
|
||||
** These are only hints. If two overlap, only one gets used
|
||||
** There is therefore no guarantee in general.
|
||||
*/
|
||||
|
||||
__Serializer.prototype.suggestPrefix = function(prefix, uri) {
|
||||
this.prefixes[uri] = prefix;
|
||||
}
|
||||
|
||||
// Takes a namespace -> prefix map
|
||||
__Serializer.prototype.suggestNamespaces = function(namespaces) {
|
||||
for (var px in namespaces) {
|
||||
this.prefixes[namespaces[px]] = px;
|
||||
}
|
||||
}
|
||||
|
||||
// Make up an unused prefix for a random namespace
|
||||
__Serializer.prototype.makeUpPrefix = function(uri) {
|
||||
var p = uri;
|
||||
var namespaces = [];
|
||||
var pok;
|
||||
var sz = this;
|
||||
|
||||
function canUse(pp) {
|
||||
if (namespaces[pp]) return false; // already used
|
||||
sz.prefixes[uri] = pp;
|
||||
pok = pp;
|
||||
return true
|
||||
}
|
||||
for (var ns in sz.prefixes) namespaces[sz.prefixes[ns]] = ns; // reverse index
|
||||
if ('#/'.indexOf(p[p.length-1]) >= 0) p = p.slice(0, -1);
|
||||
var slash = p.lastIndexOf('/');
|
||||
if (slash >= 0) p = p.slice(slash+1);
|
||||
var i = 0;
|
||||
while (i < p.length)
|
||||
if (sz.prefixchars.indexOf(p[i])) i++; else break;
|
||||
p = p.slice(0,i);
|
||||
if (p.length < 6 && canUse(p)) return pok; // exact i sbest
|
||||
if (canUse(p.slice(0,3))) return pok;
|
||||
if (canUse(p.slice(0,2))) return pok;
|
||||
if (canUse(p.slice(0,4))) return pok;
|
||||
if (canUse(p.slice(0,1))) return pok;
|
||||
if (canUse(p.slice(0,5))) return pok;
|
||||
for (var i=0;; i++) if (canUse(p.slice(0,3)+i)) return pok;
|
||||
}
|
||||
|
||||
|
||||
/* The scan is to find out which nodes will have to be the roots of trees
|
||||
** in the serialized form. This will be any symbols, and any bnodes
|
||||
** which hve more or less than one incoming arc, and any bnodes which have
|
||||
** one incoming arc but it is an uninterrupted loop of such nodes back to itself.
|
||||
** This should be kept linear time with repect to the number of statements.
|
||||
** Note it does not use any indexing.
|
||||
*/
|
||||
|
||||
|
||||
// Todo:
|
||||
// - Sort the statements by subject, pred, object
|
||||
// - do stuff about the docu first and then (or first) about its primary topic.
|
||||
|
||||
__Serializer.prototype.rootSubjects = function(sts) {
|
||||
var incoming = [];
|
||||
var subjects = [];
|
||||
var sz = this;
|
||||
|
||||
for (var i = 0; i<sts.length; i++) {
|
||||
var x = sts[i].object;
|
||||
if (!incoming[x]) incoming[x] = [];
|
||||
incoming[x].push(sts[i].subject) // List of things which will cause this to be printed
|
||||
var ss = subjects[sz.toStr(sts[i].subject)]; // Statements with this as subject
|
||||
if (!ss) ss = [];
|
||||
ss.push(sts[i]);
|
||||
subjects[this.toStr(sts[i].subject)] = ss; // Make hash. @@ too slow for formula?
|
||||
tabulator.log.debug(' sz potential subject: '+sts[i].subject)
|
||||
}
|
||||
|
||||
var roots = [];
|
||||
var loopBreakers = [];
|
||||
|
||||
function accountedFor(x, start) {
|
||||
if (x.termType != 'bnode') return true; // will be subject
|
||||
var zz = incoming[x];
|
||||
if (!zz || zz.length != 1) return true;
|
||||
if (loopBreakers[x]) return true;
|
||||
if (zz[0] == start) return false;
|
||||
return accountedFor(zz[0], start);
|
||||
}
|
||||
for (var xNT in subjects) {
|
||||
var x = sz.fromStr(xNT);
|
||||
if ((x.termType != 'bnode') || !incoming[x] || (incoming[x].length != 1)){
|
||||
roots.push(x);
|
||||
tabulator.log.debug(' sz actual subject -: ' + x)
|
||||
continue;
|
||||
}
|
||||
if (accountedFor(incoming[x][0]), x) {
|
||||
continue;
|
||||
}
|
||||
roots.push(x);
|
||||
tabulator.log.debug(' sz potential subject *: '+sts[i].subject)
|
||||
loopBreakers[x] = 1;
|
||||
}
|
||||
this.incoming = incoming; // Keep for serializing
|
||||
return [roots, subjects];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
__Serializer.prototype.toN3 = function(f) {
|
||||
return this.statementsToN3(f.statements);
|
||||
}
|
||||
|
||||
__Serializer.prototype._notQNameChars = "\t\r\n !\"#$%&'()*.,+/;<=>?@[\\]^`{|}~";
|
||||
__Serializer.prototype._notNameChars =
|
||||
( __Serializer.prototype._notQNameChars + ":" ) ;
|
||||
|
||||
|
||||
__Serializer.prototype.statementsToN3 = function(sts) {
|
||||
var indent = 4;
|
||||
var width = 80;
|
||||
// var subjects = null; // set later
|
||||
var sz = this;
|
||||
|
||||
var namespaceCounts = []; // which have been used
|
||||
|
||||
predMap = {
|
||||
'http://www.w3.org/2002/07/owl#sameAs': '=',
|
||||
'http://www.w3.org/2000/10/swap/log#implies': '=>',
|
||||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type': 'a'
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////// Arrange the bits of text
|
||||
|
||||
var spaces=function(n) {
|
||||
var s='';
|
||||
for(var i=0; i<n; i++) s+=' ';
|
||||
return s
|
||||
}
|
||||
|
||||
treeToLine = function(tree) {
|
||||
var str = '';
|
||||
for (var i=0; i<tree.length; i++) {
|
||||
var branch = tree[i];
|
||||
var s2 = (typeof branch == 'string') ? branch : treeToLine(branch);
|
||||
if (i!=0 && s2 != ',' && s2 != ';' && s2 != '.') str += ' ';
|
||||
str += s2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Convert a nested tree of lists and strings to a string
|
||||
treeToString = function(tree, level) {
|
||||
var str = '';
|
||||
var lastLength = 100000;
|
||||
if (!level) level = 0;
|
||||
for (var i=0; i<tree.length; i++) {
|
||||
var branch = tree[i];
|
||||
if (typeof branch != 'string') {
|
||||
var substr = treeToString(branch, level +1);
|
||||
if (
|
||||
substr.length < 10*(width-indent*level)
|
||||
&& substr.indexOf('"""') < 0) {// Don't mess up multiline strings
|
||||
var line = treeToLine(branch);
|
||||
if (line.length < (width-indent*level)) {
|
||||
branch = ' '+line; // @@ Hack: treat as string below
|
||||
substr = ''
|
||||
}
|
||||
}
|
||||
if (substr) lastLength = 10000;
|
||||
str += substr;
|
||||
}
|
||||
if (typeof branch == 'string') {
|
||||
if (branch.length == '1' && str.slice(-1) == '\n') {
|
||||
if (",.;".indexOf(branch) >=0) {
|
||||
str = str.slice(0,-1) + branch + '\n'; // slip punct'n on end
|
||||
lastLength += 1;
|
||||
continue;
|
||||
} else if ("])}".indexOf(branch) >=0) {
|
||||
str = str.slice(0,-1) + ' ' + branch + '\n';
|
||||
lastLength += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (lastLength < (indent*level+4)) { // continue
|
||||
str = str.slice(0,-1) + ' ' + branch + '\n';
|
||||
lastLength += branch.length + 1;
|
||||
} else {
|
||||
var line = spaces(indent*level) +branch;
|
||||
str += line +'\n';
|
||||
lastLength = line.length;
|
||||
}
|
||||
|
||||
} else { // not string
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////// Structure for N3
|
||||
|
||||
|
||||
function statementListToTree(statements) {
|
||||
// print('Statement tree for '+statements.length);
|
||||
var res = [];
|
||||
var pair = sz.rootSubjects(statements);
|
||||
var roots = pair[0];
|
||||
// print('Roots: '+roots)
|
||||
var subjects = pair[1];
|
||||
var results = []
|
||||
for (var i=0; i<roots.length; i++) {
|
||||
var root = roots[i];
|
||||
results.push(subjectTree(root, subjects))
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// The tree for a subject
|
||||
function subjectTree(subject, subjects) {
|
||||
if (subject.termType == 'bnode' && !sz.incoming[subject])
|
||||
return objectTree(subject, subjects).concat(["."]); // Anonymous bnode subject
|
||||
return [ termToN3(subject, subjects) ].concat([propertyTree(subject, subjects)]).concat(["."]);
|
||||
}
|
||||
|
||||
|
||||
// The property tree for a single subject or anonymous node
|
||||
function propertyTree(subject, subjects) {
|
||||
// print('Proprty tree for '+subject);
|
||||
var results = []
|
||||
var lastPred = null;
|
||||
var sts = subjects[sz.toStr(subject)]; // relevant statements
|
||||
if (typeof sts == 'undefined') {
|
||||
alert('Cant find statements for '+subject);
|
||||
}
|
||||
sts.sort();
|
||||
var objects = [];
|
||||
for (var i=0; i<sts.length; i++) {
|
||||
var st = sts[i];
|
||||
if (st.predicate.uri == lastPred) {
|
||||
objects.push(',');
|
||||
} else {
|
||||
if (lastPred) {
|
||||
results=results.concat([objects]).concat([';']);
|
||||
objects = [];
|
||||
}
|
||||
results.push(predMap[st.predicate.uri] ?
|
||||
predMap[st.predicate.uri] : termToN3(st.predicate, subjects));
|
||||
}
|
||||
lastPred = st.predicate.uri;
|
||||
objects.push(objectTree(st.object, subjects));
|
||||
}
|
||||
results=results.concat([objects]);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Convert a set of statements into a nested tree of lists and strings
|
||||
function objectTree(obj, subjects) {
|
||||
if (obj.termType == 'bnode' && subjects[sz.toStr(obj)]) // and there are statements
|
||||
return ['['].concat(propertyTree(obj, subjects)).concat([']']);
|
||||
return termToN3(obj, subjects);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////// Atomic Terms
|
||||
|
||||
// Deal with term level things and nesting with no bnode structure
|
||||
|
||||
function termToN3(expr, subjects) {
|
||||
switch(expr.termType) {
|
||||
case 'bnode':
|
||||
case 'variable': return expr.toNT();
|
||||
case 'literal':
|
||||
var str = stringToN3(expr.value);
|
||||
if (expr.lang) str+= '@' + expr.lang;
|
||||
if (expr.dt) str+= '^^' + termToN3(expr.dt, subjects);
|
||||
return str;
|
||||
case 'symbol':
|
||||
return symbolToN3(expr.uri);
|
||||
case 'formula':
|
||||
var res = ['{'];
|
||||
res = res.concat(statementListToTree(expr.statements));
|
||||
return res.concat(['}']);
|
||||
case 'collection':
|
||||
var res = ['('];
|
||||
for (i=0; i<expr.elements.length; i++) {
|
||||
res.push( [ objectTree(expr.elements[i], subjects) ]);
|
||||
}
|
||||
res.push(')');
|
||||
return res;
|
||||
|
||||
default:
|
||||
throw "Internal: termToN3 cannot handle "+expr+" of termType+"+expr.termType
|
||||
return ''+expr;
|
||||
}
|
||||
}
|
||||
|
||||
function symbolToN3(uri) { // c.f. symbolString() in notation3.py
|
||||
var j = uri.indexOf('#');
|
||||
if (j<0 && sz.flags.indexOf('/') < 0) {
|
||||
j = uri.lastIndexOf('/');
|
||||
}
|
||||
if (j >= 0 && sz.flags.indexOf('p') < 0) { // Can split at namespace
|
||||
var canSplit = true;
|
||||
for (var k=j+1; k<uri.length; k++) {
|
||||
if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >=0) {
|
||||
canSplit = false; break;
|
||||
}
|
||||
}
|
||||
if (canSplit) {
|
||||
var localid = uri.slice(j+1);
|
||||
var namesp = uri.slice(0,j+1);
|
||||
if (sz.defaultNamespace && sz.defaultNamespace == namesp
|
||||
&& sz.flags.indexOf('d') < 0) {// d -> suppress default
|
||||
if (sz.flags.indexOf('k') >= 0 &&
|
||||
sz.keyords.indexOf(localid) <0)
|
||||
return localid;
|
||||
return ':' + localid;
|
||||
}
|
||||
var prefix = sz.prefixes[namesp];
|
||||
if (prefix) {
|
||||
namespaceCounts[namesp] = true;
|
||||
return prefix + ':' + localid;
|
||||
}
|
||||
if (uri.slice(0, j) == sz.base)
|
||||
return '<#' + localid + '>';
|
||||
// Fall though if can't do qname
|
||||
}
|
||||
}
|
||||
if (sz.flags.indexOf('r') < 0 && sz.base)
|
||||
uri = Util.uri.refTo(sz.base, uri);
|
||||
else if (sz.flags.indexOf('u') >= 0)
|
||||
uri = backslashUify(uri);
|
||||
else uri = hexify(uri);
|
||||
return '<'+uri+'>';
|
||||
}
|
||||
|
||||
function prefixDirectives() {
|
||||
str = '';
|
||||
if (sz.defaultNamespace)
|
||||
str += '@prefix : <'+sz.defaultNamespace+'>.\n';
|
||||
for (var ns in namespaceCounts) {
|
||||
str += '@prefix ' + sz.prefixes[ns] + ': <'+ns+'>.\n';
|
||||
}
|
||||
return str + '\n';
|
||||
}
|
||||
|
||||
// stringToN3: String escaping for N3
|
||||
//
|
||||
var forbidden1 = new RegExp(/[\\"\b\f\r\v\t\n\u0080-\uffff]/gm);
|
||||
var forbidden3 = new RegExp(/[\\"\b\f\r\v\u0080-\uffff]/gm);
|
||||
function stringToN3(str, flags) {
|
||||
if (!flags) flags = "e";
|
||||
var res = '', i=0, j=0;
|
||||
var delim;
|
||||
var forbidden;
|
||||
if (str.length > 20 // Long enough to make sense
|
||||
&& str.slice(-1) != '"' // corner case'
|
||||
&& flags.indexOf('n') <0 // Force single line
|
||||
&& (str.indexOf('\n') >0 || str.indexOf('"') > 0)) {
|
||||
delim = '"""';
|
||||
forbidden = forbidden3;
|
||||
} else {
|
||||
delim = '"';
|
||||
forbidden = forbidden1;
|
||||
}
|
||||
for(i=0; i<str.length;) {
|
||||
forbidden.lastIndex = 0;
|
||||
var m = forbidden.exec(str.slice(i));
|
||||
if (m == null) break;
|
||||
j = i + forbidden.lastIndex -1;
|
||||
res += str.slice(i,j);
|
||||
var ch = str[j];
|
||||
if (ch=='"' && delim == '"""' && str.slice(j,j+3) != '"""') {
|
||||
res += ch;
|
||||
} else {
|
||||
var k = '\b\f\r\t\v\n\\"'.indexOf(ch); // No escaping of bell (7)?
|
||||
if (k >= 0) {
|
||||
res += "\\" + 'bfrtvn\\"'[k];
|
||||
} else {
|
||||
if (flags.indexOf('e')>=0) {
|
||||
res += '\\u' + ('000'+
|
||||
ch.charCodeAt(0).toString(16).toLowerCase()).slice(-4)
|
||||
} else { // no 'e' flag
|
||||
res += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
i = j+1;
|
||||
}
|
||||
return delim + res + str.slice(i) + delim
|
||||
}
|
||||
|
||||
// Body of toN3:
|
||||
|
||||
var tree = statementListToTree(sts);
|
||||
return prefixDirectives() + treeToString(tree, -1);
|
||||
|
||||
}
|
||||
|
||||
// String ecaping utilities
|
||||
|
||||
function hexify(str) { // also used in parser
|
||||
// var res = '';
|
||||
// for (var i=0; i<str.length; i++) {
|
||||
// k = str.charCodeAt(i);
|
||||
// if (k>126 || k<33)
|
||||
// res += '%' + ('0'+n.toString(16)).slice(-2); // convert to upper?
|
||||
// else
|
||||
// res += str[i];
|
||||
// }
|
||||
// return res;
|
||||
return encodeURI(str);
|
||||
}
|
||||
|
||||
|
||||
function backslashUify(str) {
|
||||
var res = '';
|
||||
for (var i=0; i<str.length; i++) {
|
||||
k = str.charCodeAt(i);
|
||||
if (k>65535)
|
||||
res += '\U' + ('00000000'+n.toString(16)).slice(-8); // convert to upper?
|
||||
else if (k>126)
|
||||
res += '\u' + ('0000'+n.toString(16)).slice(-4);
|
||||
else
|
||||
res += str[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////// XML serialization
|
||||
|
||||
__Serializer.prototype.statementsToXML = function(sts) {
|
||||
var indent = 4;
|
||||
var width = 80;
|
||||
// var subjects = null; // set later
|
||||
var sz = this;
|
||||
|
||||
var namespaceCounts = []; // which have been used
|
||||
namespaceCounts['http://www.w3.org/1999/02/22-rdf-syntax-ns#'] = true;
|
||||
|
||||
////////////////////////// Arrange the bits of XML text
|
||||
|
||||
var spaces=function(n) {
|
||||
var s='';
|
||||
for(var i=0; i<n; i++) s+=' ';
|
||||
return s
|
||||
}
|
||||
|
||||
XMLtreeToLine = function(tree) {
|
||||
var str = '';
|
||||
for (var i=0; i<tree.length; i++) {
|
||||
var branch = tree[i];
|
||||
var s2 = (typeof branch == 'string') ? branch : XMLtreeToLine(branch);
|
||||
str += s2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Convert a nested tree of lists and strings to a string
|
||||
XMLtreeToString = function(tree, level) {
|
||||
var str = '';
|
||||
var lastLength = 100000;
|
||||
if (!level) level = 0;
|
||||
for (var i=0; i<tree.length; i++) {
|
||||
var branch = tree[i];
|
||||
if (typeof branch != 'string') {
|
||||
var substr = XMLtreeToString(branch, level +1);
|
||||
if (
|
||||
substr.length < 10*(width-indent*level)
|
||||
&& substr.indexOf('"""') < 0) {// Don't mess up multiline strings
|
||||
var line = XMLtreeToLine(branch);
|
||||
if (line.length < (width-indent*level)) {
|
||||
branch = ' '+line; // @@ Hack: treat as string below
|
||||
substr = ''
|
||||
}
|
||||
}
|
||||
if (substr) lastLength = 10000;
|
||||
str += substr;
|
||||
}
|
||||
if (typeof branch == 'string') {
|
||||
if (lastLength < (indent*level+4)) { // continue
|
||||
str = str.slice(0,-1) + ' ' + branch + '\n';
|
||||
lastLength += branch.length + 1;
|
||||
} else {
|
||||
var line = spaces(indent*level) +branch;
|
||||
str += line +'\n';
|
||||
lastLength = line.length;
|
||||
}
|
||||
|
||||
} else { // not string
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
function statementListToXMLTree(statements) {
|
||||
sz.suggestPrefix('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
|
||||
var res = [];
|
||||
var pair = sz.rootSubjects(statements);
|
||||
var roots = pair[0];
|
||||
var subjects = pair[1];
|
||||
results = []
|
||||
for (var i=0; i<roots.length; i++) {
|
||||
root = roots[i];
|
||||
results.push(subjectXMLTree(root, subjects))
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function escapeForXML(str) {
|
||||
if (typeof str == 'undefined') return '@@@undefined@@@@';
|
||||
return str.replace(/&/g, '&').replace(/</g, '<')
|
||||
}
|
||||
|
||||
function relURI(term) {
|
||||
return escapeForXML((sz.base) ? Util.uri.refTo(this.base, term.uri) : term.uri);
|
||||
}
|
||||
|
||||
// The tree for a subject
|
||||
function subjectXMLTree(subject, subjects, referenced) {
|
||||
const liPrefix = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_';
|
||||
|
||||
var type = null;
|
||||
|
||||
var results = []
|
||||
var sts = subjects[sz.toStr(subject)]; // relevant statements
|
||||
sts.sort();
|
||||
for (var i=0; i<sts.length; i++) {
|
||||
var st = sts[i];
|
||||
|
||||
if(st.predicate.uri == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' && !type && st.object.termType == "symbol") {
|
||||
// look for a type
|
||||
type = st.object;
|
||||
} else {
|
||||
// see whether predicate can be replaced with "li"
|
||||
if(st.predicate.uri.substr(0, liPrefix.length) == liPrefix) {
|
||||
var number = st.predicate.uri.substr(liPrefix.length);
|
||||
|
||||
// make sure these are actually numeric list items
|
||||
var intNumber = parseInt(number);
|
||||
if(number == intNumber.toString()) {
|
||||
// was numeric; don't need to worry about ordering since we've already
|
||||
// sorted the statements
|
||||
st.predicate = RDFSymbol('http://www.w3.org/1999/02/22-rdf-syntax-ns#li');
|
||||
}
|
||||
}
|
||||
|
||||
switch (st.object.termType) {
|
||||
case 'bnode':
|
||||
results = results.concat(['<'+qname(st.predicate)+'>',
|
||||
subjectXMLTree(st.object, subjects, true),
|
||||
'</'+qname(st.predicate)+'>']);
|
||||
break;
|
||||
case 'symbol':
|
||||
results = results.concat(['<'+qname(st.predicate)+' rdf:resource="'
|
||||
+ relURI(st.object)+'"/>']);
|
||||
break;
|
||||
case 'literal':
|
||||
results = results.concat(['<'+qname(st.predicate)
|
||||
+ (st.object.dt ? ' rdf:datatype="'+escapeForXML(st.object.dt.uri)+'"' : '')
|
||||
+ (st.object.lang ? ' xml:lang="'+st.object.lang+'"' : '')
|
||||
+ '>' + escapeForXML(st.object.value)
|
||||
+ '</'+qname(st.predicate)+'>']);
|
||||
break;
|
||||
case 'collection':
|
||||
results = results.concat(['<'+qname(st.predicate)+' rdf:parseType="Collection">',
|
||||
collectionXMLTree(st.object, subjects),
|
||||
'</'+qname(st.predicate)+'>']);
|
||||
break;
|
||||
default:
|
||||
throw "Can't serialize object of type "+st.object.termType +" into XML";
|
||||
|
||||
} // switch
|
||||
}
|
||||
}
|
||||
|
||||
var tag = type ? qname(type) : 'rdf:Description';
|
||||
|
||||
attrs = '';
|
||||
if (subject.termType == 'bnode') {
|
||||
if(!referenced || sz.incoming[subject].length != 1) { // not an anonymous bnode
|
||||
attrs = ' rdf:ID="'+subject.toNT().slice(2)+'"';
|
||||
}
|
||||
} else {
|
||||
attrs = ' rdf:about="'+ relURI(subject)+'"';
|
||||
}
|
||||
|
||||
return [ '<' + tag + attrs + '>' ].concat([results]).concat(["</"+ tag +">"]);
|
||||
}
|
||||
|
||||
function collectionXMLTree(subject, subjects) {
|
||||
res = []
|
||||
for (var i=0; i< subject.elements.length; i++) {
|
||||
res.push(subjectXMLTree(subject.elements[i], subjects));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function qname(term) {
|
||||
var uri = term.uri;
|
||||
|
||||
var j = uri.indexOf('#');
|
||||
if (j<0 && sz.flags.indexOf('/') < 0) {
|
||||
j = uri.lastIndexOf('/');
|
||||
}
|
||||
if (j < 0) throw ("Cannot make qname out of <"+uri+">")
|
||||
|
||||
var canSplit = true;
|
||||
for (var k=j+1; k<uri.length; k++) {
|
||||
if (__Serializer.prototype._notNameChars.indexOf(uri[k]) >=0) {
|
||||
throw ('Invalid character "'+uri[k] +'" cannot be in XML qname for URI: '+uri);
|
||||
}
|
||||
}
|
||||
var localid = uri.slice(j+1);
|
||||
var namesp = uri.slice(0,j+1);
|
||||
if (sz.defaultNamespace && sz.defaultNamespace == namesp
|
||||
&& sz.flags.indexOf('d') < 0) {// d -> suppress default
|
||||
return localid;
|
||||
}
|
||||
var prefix = sz.prefixes[namesp];
|
||||
if (!prefix) prefix = sz.makeUpPrefix(namesp);
|
||||
namespaceCounts[namesp] = true;
|
||||
return prefix + ':' + localid;
|
||||
// throw ('No prefix for namespace "'+namesp +'" for XML qname for '+uri+', namespaces: '+sz.prefixes+' sz='+sz);
|
||||
}
|
||||
|
||||
// Body of toXML:
|
||||
|
||||
var tree = statementListToXMLTree(sts);
|
||||
var str = '<rdf:RDF';
|
||||
if (sz.defaultNamespace)
|
||||
str += ' xmlns="'+escapeForXML(sz.defaultNamespace)+'"';
|
||||
for (var ns in namespaceCounts) {
|
||||
str += '\n xmlns:' + sz.prefixes[ns] + '="'+escapeForXML(ns)+'"';
|
||||
}
|
||||
str += '>';
|
||||
|
||||
var tree2 = [str, tree, '</rdf:RDF>']; //@@ namespace declrations
|
||||
return XMLtreeToString(tree2, -1);
|
||||
|
||||
|
||||
} // End @@ body
|
||||
|
313
chrome/content/zotero/xpcom/rdf/term.js
Normal file
313
chrome/content/zotero/xpcom/rdf/term.js
Normal file
|
@ -0,0 +1,313 @@
|
|||
// These are the classes corresponding to the RDF and N3 data models
|
||||
//
|
||||
// Designed to look like rdflib and cwm designs.
|
||||
//
|
||||
// Issues: Should the names start with RDF to make them
|
||||
// unique as program-wide symbols?
|
||||
//
|
||||
// W3C open source licence 2005.
|
||||
//
|
||||
|
||||
RDFTracking = 0 // Are we requiring reasons for statements?
|
||||
|
||||
//takes in an object and makes it an object if it's a literal
|
||||
function makeTerm(val) {
|
||||
// tabulator.log.debug("Making term from " + val)
|
||||
if (typeof val == 'object') return val;
|
||||
if (typeof val == 'string') return new RDFLiteral(val);
|
||||
if (typeof val == 'number') return new RDFLiteral(val); // @@ differet types
|
||||
if (typeof val == 'boolean') return new RDFLiteral(val?"1":"0", undefined,
|
||||
RDFSymbol.prototype.XSDboolean);
|
||||
if (typeof val == 'undefined') return undefined;
|
||||
alert("Can't make term from " + val + " of type " + typeof val);
|
||||
}
|
||||
|
||||
|
||||
// Symbol
|
||||
|
||||
function RDFEmpty() {
|
||||
return this;
|
||||
}
|
||||
RDFEmpty.prototype.termType = 'empty'
|
||||
RDFEmpty.prototype.toString = function () { return "()" }
|
||||
RDFEmpty.prototype.toNT = function () { return "@@" }
|
||||
|
||||
function RDFSymbol_toNT(x) {
|
||||
return ("<" + x.uri + ">")
|
||||
}
|
||||
|
||||
function toNT() {
|
||||
return RDFSymbol_toNT(this)
|
||||
}
|
||||
|
||||
function RDFSymbol(uri) {
|
||||
this.uri = uri
|
||||
return this
|
||||
}
|
||||
|
||||
RDFSymbol.prototype.termType = 'symbol'
|
||||
RDFSymbol.prototype.toString = toNT
|
||||
RDFSymbol.prototype.toNT = toNT
|
||||
|
||||
// Some precalculaued symbols
|
||||
|
||||
RDFSymbol.prototype.XSDboolean = new RDFSymbol('http://www.w3.org/2001/XMLSchema#boolean');
|
||||
RDFSymbol.prototype.integer = new RDFSymbol('http://www.w3.org/2001/XMLSchema#integer');
|
||||
|
||||
|
||||
// Blank Node
|
||||
|
||||
var RDFNextId = 0; // Gobal genid
|
||||
RDFGenidPrefix = "genid:"
|
||||
NTAnonymousNodePrefix = "_:n"
|
||||
|
||||
function RDFBlankNode(id) {
|
||||
/*if (id)
|
||||
this.id = id;
|
||||
else*/
|
||||
this.id = RDFNextId++
|
||||
return this
|
||||
}
|
||||
|
||||
RDFBlankNode.prototype.termType = 'bnode'
|
||||
|
||||
RDFBlankNode.prototype.toNT = function() {
|
||||
return NTAnonymousNodePrefix + this.id
|
||||
}
|
||||
RDFBlankNode.prototype.toString = RDFBlankNode.prototype.toNT
|
||||
|
||||
// Literal
|
||||
|
||||
function RDFLiteral(value, lang, datatype) {
|
||||
this.value = value
|
||||
this.lang=lang; // string
|
||||
this.datatype=datatype; // term
|
||||
this.toString = RDFLiteralToString
|
||||
this.toNT = RDFLiteral_toNT
|
||||
return this
|
||||
}
|
||||
|
||||
RDFLiteral.prototype.termType = 'literal'
|
||||
|
||||
function RDFLiteral_toNT() {
|
||||
var str = this.value
|
||||
if (typeof str != 'string') {
|
||||
if (typeof str == 'number') return ''+str;
|
||||
throw Error("Value of RDF literal is not string: "+str)
|
||||
}
|
||||
str = str.replace(/\\/g, '\\\\'); // escape
|
||||
str = str.replace(/\"/g, '\\"');
|
||||
str = '"' + str + '"' //'
|
||||
|
||||
if (this.datatype){
|
||||
str = str + '^^' + this.datatype//.toNT()
|
||||
}
|
||||
if (this.lang) {
|
||||
str = str + "@" + this.lang
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function RDFLiteralToString() {
|
||||
return ''+this.value
|
||||
}
|
||||
|
||||
RDFLiteral.prototype.toString = RDFLiteralToString
|
||||
RDFLiteral.prototype.toNT = RDFLiteral_toNT
|
||||
|
||||
function RDFCollection() {
|
||||
this.id = RDFNextId++
|
||||
this.elements = []
|
||||
this.closed = false
|
||||
}
|
||||
|
||||
RDFCollection.prototype.termType = 'collection'
|
||||
|
||||
RDFCollection.prototype.toNT = function() {
|
||||
return NTAnonymousNodePrefix + this.id
|
||||
}
|
||||
RDFCollection.prototype.toString = RDFCollection.prototype.toNT
|
||||
|
||||
RDFCollection.prototype.append = function (el) {
|
||||
this.elements.push(el)
|
||||
}
|
||||
RDFCollection.prototype.unshift=function(el){
|
||||
this.elements.unshift(el);
|
||||
}
|
||||
RDFCollection.prototype.shift=function(){
|
||||
return this.elements.shift();
|
||||
}
|
||||
|
||||
RDFCollection.prototype.close = function () {
|
||||
this.closed = true
|
||||
}
|
||||
|
||||
// Statement
|
||||
//
|
||||
// This is a triple with an optional reason.
|
||||
//
|
||||
// The reason can point to provenece or inference
|
||||
//
|
||||
function RDFStatement_toNT() {
|
||||
return (this.subject.toNT() + " "
|
||||
+ this.predicate.toNT() + " "
|
||||
+ this.object.toNT() +" .")
|
||||
}
|
||||
|
||||
function RDFStatement(subject, predicate, object, why) {
|
||||
this.subject = makeTerm(subject)
|
||||
this.predicate = makeTerm(predicate)
|
||||
this.object = makeTerm(object)
|
||||
if (typeof why !='undefined') {
|
||||
this.why = why
|
||||
} else if (RDFTracking) {
|
||||
tabulator.log.debug("WARNING: No reason on "+subject+" "+predicate+" "+object)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
RDFStatement.prototype.toNT = RDFStatement_toNT
|
||||
RDFStatement.prototype.toString = RDFStatement_toNT
|
||||
|
||||
|
||||
// Formula
|
||||
//
|
||||
// Set of statements.
|
||||
|
||||
function RDFFormula() {
|
||||
this.statements = []
|
||||
this.constraints = []
|
||||
this.initBindings = []
|
||||
this.optional = []
|
||||
this.superFormula = null;
|
||||
return this
|
||||
}
|
||||
|
||||
function RDFFormula_toNT() {
|
||||
// throw 'Who called me?';
|
||||
return "{" + this.statements.join('\n') + "}"
|
||||
}
|
||||
|
||||
//RDFQueryFormula.prototype = new RDFFormula()
|
||||
//RDFQueryFormula.termType = 'queryFormula'
|
||||
RDFFormula.prototype.termType = 'formula'
|
||||
RDFFormula.prototype.toNT = RDFFormula_toNT
|
||||
RDFFormula.prototype.toString = RDFFormula_toNT
|
||||
|
||||
RDFFormula.prototype.add = function(subj, pred, obj, why) {
|
||||
this.statements.push(new RDFStatement(subj, pred, obj, why))
|
||||
}
|
||||
|
||||
// Convenience methods on a formula allow the creation of new RDF terms:
|
||||
|
||||
RDFFormula.prototype.sym = function(uri,name) {
|
||||
if (name != null) {
|
||||
if (!tabulator.ns[uri]) throw 'The prefix "'+uri+'" is not set in the API';
|
||||
uri = tabulator.ns[uri] + name
|
||||
}
|
||||
return new RDFSymbol(uri)
|
||||
}
|
||||
|
||||
RDFFormula.prototype.literal = function(val, lang, dt) {
|
||||
return new RDFLiteral(val.toString(), lang, dt)
|
||||
}
|
||||
|
||||
RDFFormula.prototype.bnode = function(id) {
|
||||
return new RDFBlankNode(id)
|
||||
}
|
||||
|
||||
RDFFormula.prototype.formula = function() {
|
||||
return new RDFFormula()
|
||||
}
|
||||
|
||||
RDFFormula.prototype.collection = function () { // obsolete
|
||||
return new RDFCollection()
|
||||
}
|
||||
|
||||
RDFFormula.prototype.list = function (values) {
|
||||
li = new RDFCollection();
|
||||
if (values) {
|
||||
for(var i = 0; i<values.length; i++) {
|
||||
li.append(values[i]);
|
||||
}
|
||||
}
|
||||
return li;
|
||||
}
|
||||
|
||||
RDFFormula.instances={};
|
||||
RDFFormula.prototype.registerFormula = function(accesskey){
|
||||
var superFormula = this.superFormula || this;
|
||||
RDFFormula.instances[accesskey] = this;
|
||||
var formulaTerm = superFormula.bnode();
|
||||
superFormula.add(formulaTerm, tabulator.ns.rdf('type'),superFormula.sym("http://www.w3.org/2000/10/swap/log#Formula"));
|
||||
superFormula.add(formulaTerm, tabulator.ns.foaf('name'), superFormula.literal(accesskey));
|
||||
superFormula.add(formulaTerm, tabulator.ns.link('accesskey'), superFormula.literal(accesskey));
|
||||
//RDFFormula.instances.push("accesskey");
|
||||
}
|
||||
|
||||
|
||||
/* Variable
|
||||
**
|
||||
** Variables are placeholders used in patterns to be matched.
|
||||
** In cwm they are symbols which are the formula's list of quantified variables.
|
||||
** In sparl they are not visibily URIs. Here we compromise, by having
|
||||
** a common special base URI for variables.
|
||||
*/
|
||||
|
||||
RDFVariableBase = "varid:"; // We deem variabe x to be the symbol varid:x
|
||||
|
||||
function RDFVariable(rel) {
|
||||
this.uri = URIjoin(rel, RDFVariableBase);
|
||||
return this;
|
||||
}
|
||||
|
||||
RDFVariable.prototype.termType = 'variable';
|
||||
RDFVariable.prototype.toNT = function() {
|
||||
if (this.uri.slice(0, RDFVariableBase.length) == RDFVariableBase) {
|
||||
return '?'+ this.uri.slice(RDFVariableBase.length);} // @@ poor man's refTo
|
||||
return '?' + this.uri;
|
||||
};
|
||||
|
||||
RDFVariable.prototype.toString = RDFVariable.prototype.toNT;
|
||||
RDFVariable.prototype.classOrder = 7;
|
||||
|
||||
RDFFormula.prototype.variable = function(name) {
|
||||
return new RDFVariable(name);
|
||||
};
|
||||
|
||||
RDFVariable.prototype.hashString = RDFVariable.prototype.toNT;
|
||||
|
||||
|
||||
// The namespace function generator
|
||||
|
||||
function RDFNamespace(nsuri) {
|
||||
return function(ln) { return new RDFSymbol(nsuri+(ln===undefined?'':ln)) }
|
||||
}
|
||||
|
||||
RDFFormula.prototype.ns = function(nsuri) {
|
||||
return function(ln) { return new RDFSymbol(nsuri+(ln===undefined?'':ln)) }
|
||||
}
|
||||
|
||||
|
||||
// Parse a single token
|
||||
//
|
||||
// The bnode bit should not be used on program-external values; designed
|
||||
// for internal work such as storing a bnode id in an HTML attribute.
|
||||
// Not coded for literals.
|
||||
|
||||
RDFFormula.prototype.fromNT = function(str) {
|
||||
var len = str.length
|
||||
var ch = str.slice(0,1)
|
||||
if (ch == '<') return this.sym(str.slice(1,len-1))
|
||||
if (ch == '_') {
|
||||
var x = new RDFBlankNode();
|
||||
x.id = parseInt(str.slice(3));
|
||||
RDFNextId--
|
||||
return x
|
||||
}
|
||||
throw "Can't convert from NT"+str;
|
||||
|
||||
//alert("Can't yet convert from NT: '"+str+"', "+str[0])
|
||||
}
|
||||
|
||||
// ends
|
143
chrome/content/zotero/xpcom/rdf/uri.js
Normal file
143
chrome/content/zotero/xpcom/rdf/uri.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Implementing URI-specific functions
|
||||
//
|
||||
// See RFC 2386
|
||||
//
|
||||
// This is or was http://www.w3.org/2005/10/ajaw/uri.js
|
||||
// 2005 W3C open source licence
|
||||
//
|
||||
//
|
||||
// Take a URI given in relative or absolute form and a base
|
||||
// URI, and return an absolute URI
|
||||
//
|
||||
// See also http://www.w3.org/2000/10/swap/uripath.py
|
||||
//
|
||||
|
||||
if (typeof Util == "undefined") { Util = {}}
|
||||
if (typeof Util.uri == "undefined") { Util.uri = {}}
|
||||
|
||||
Util.uri.join = function (given, base) {
|
||||
// if (typeof tabulator.log.debug != 'undefined') tabulator.log.debug(" URI given="+given+" base="+base)
|
||||
var baseHash = base.indexOf('#')
|
||||
if (baseHash > 0) base = base.slice(0, baseHash)
|
||||
if (given.length==0) return base // before chopping its filename off
|
||||
if (given.indexOf('#')==0) return base + given
|
||||
var colon = given.indexOf(':')
|
||||
if (colon >= 0) return given // Absolute URI form overrides base URI
|
||||
var baseColon = base.indexOf(':')
|
||||
if (base == "") return given;
|
||||
if (baseColon < 0) {
|
||||
alert("Invalid base: "+ base + ' in join with ' +given);
|
||||
return given
|
||||
}
|
||||
var baseScheme = base.slice(0,baseColon+1) // eg http:
|
||||
if (given.indexOf("//") == 0) // Starts with //
|
||||
return baseScheme + given;
|
||||
if (base.indexOf('//', baseColon)==baseColon+1) { // Any hostpart?
|
||||
var baseSingle = base.indexOf("/", baseColon+3)
|
||||
if (baseSingle < 0) {
|
||||
if (base.length-baseColon-3 > 0) {
|
||||
return base + "/" + given
|
||||
} else {
|
||||
return baseScheme + given
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var baseSingle = base.indexOf("/", baseColon+1)
|
||||
if (baseSingle < 0) {
|
||||
if (base.length-baseColon-1 > 0) {
|
||||
return base + "/" + given
|
||||
} else {
|
||||
return baseScheme + given
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (given.indexOf('/') == 0) // starts with / but not //
|
||||
return base.slice(0, baseSingle) + given
|
||||
|
||||
var path = base.slice(baseSingle)
|
||||
var lastSlash = path.lastIndexOf("/")
|
||||
if (lastSlash <0) return baseScheme + given
|
||||
if ((lastSlash >=0) && (lastSlash < (path.length-1)))
|
||||
path = path.slice(0, lastSlash+1) // Chop trailing filename from base
|
||||
|
||||
path = path + given
|
||||
while (path.match(/[^\/]*\/\.\.\//)) // must apply to result of prev
|
||||
path = path.replace( /[^\/]*\/\.\.\//, '') // ECMAscript spec 7.8.5
|
||||
path = path.replace( /\.\//g, '') // spec vague on escaping
|
||||
path = path.replace( /\/\.$/, '/' )
|
||||
return base.slice(0, baseSingle) + path
|
||||
}
|
||||
|
||||
var tIOService;
|
||||
if (typeof( isExtension ) != "undefined" && isExtension) {
|
||||
tIOService = Components.classes['@mozilla.org/network/io-service;1']
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
Util.uri.join2 = function (given, base){
|
||||
var baseURI = tIOService.newURI(base, null, null);
|
||||
return tIOService.newURI(baseURI.resolve(given), null, null).spec;
|
||||
}
|
||||
} else
|
||||
Util.uri.join2 = Util.uri.join;
|
||||
|
||||
// refTo: Make a URI relative to a given base
|
||||
//
|
||||
// based on code in http://www.w3.org/2000/10/swap/uripath.py
|
||||
//
|
||||
Util.uri.commonHost = new RegExp("^[-_a-zA-Z0-9.]+:(//[^/]*)?/[^/]*$");
|
||||
Util.uri.refTo = function(base, uri) {
|
||||
if (!base) return uri;
|
||||
if (base == uri) return "";
|
||||
var i =0; // How much are they identical?
|
||||
while (i<uri.length && i < base.length)
|
||||
if (uri[i] == base[i]) i++;
|
||||
else break;
|
||||
if (base.slice(0,i).match(Util.uri.commonHost)) {
|
||||
var k = uri.indexOf('//');
|
||||
if (k<0) k=-2; // no host
|
||||
var l = uri.indexOf('/', k+2); // First *single* slash
|
||||
if (uri.slice(l+1, l+2) != '/' && base.slice(l+1, l+2) != '/'
|
||||
&& uri.slice(0,l) == base.slice(0,l)) // common path to single slash
|
||||
return uri.slice(l); // but no other common path segments
|
||||
}
|
||||
// fragment of base?
|
||||
if (uri.slice(i, i+1) == '#' && base.length == i) return uri.slice(i);
|
||||
while (i>0 && uri[i-1] != '/') i--;
|
||||
|
||||
if (i<3) return uri; // No way
|
||||
if ((base.indexOf('//', i-2) > 0) || uri.indexOf('//', i-2) > 0)
|
||||
return uri; // an unshared '//'
|
||||
if (base.indexOf(':', i) >0) return uri; // unshared ':'
|
||||
var n = 0;
|
||||
for (var j=i; j<base.length; j++) if (base[j]=='/') n++;
|
||||
if (n==0 && i < uri.length && uri[i] =='#') return './' + uri.slice(i);
|
||||
if (n==0 && i == uri.length) return './';
|
||||
var str = '';
|
||||
for (var j=0; j<n; j++) str+= '../';
|
||||
return str + uri.slice(i);
|
||||
}
|
||||
|
||||
|
||||
/** returns URI without the frag **/
|
||||
Util.uri.docpart = function (uri) {
|
||||
var i = uri.indexOf("#")
|
||||
if (i < 0) return uri
|
||||
return uri.slice(0,i)
|
||||
}
|
||||
|
||||
/** return the protocol of a uri **/
|
||||
/** return null if there isn't one **/
|
||||
Util.uri.protocol = function (uri) {
|
||||
var index = uri.indexOf(':');
|
||||
if (index >= 0)
|
||||
return uri.slice(0, index);
|
||||
else
|
||||
return null;
|
||||
} //protocol
|
||||
|
||||
URIjoin = Util.uri.join
|
||||
uri_docpart = Util.uri.docpart
|
||||
uri_protocol = Util.uri.protocol
|
||||
|
||||
|
||||
//ends
|
|
@ -312,9 +312,6 @@ Zotero.Translator.prototype.logError = function(message, type, line, lineNumber,
|
|||
* Zotero.Translate: a class for translation of Zotero metadata from and to
|
||||
* other formats
|
||||
*
|
||||
* eventually, Zotero.Ingester may be rolled in here (i.e., after we get rid
|
||||
* of RDF)
|
||||
*
|
||||
* type can be:
|
||||
* export
|
||||
* import
|
||||
|
@ -958,7 +955,7 @@ Zotero.Translate.prototype._setSandboxMode = function(mode) {
|
|||
* valid: import, export
|
||||
* options: rdf, block, line
|
||||
* purpose: selects whether write/read behave as standard text functions or
|
||||
* using Mozilla's built-in support for RDF data sources
|
||||
* use an RDF data source
|
||||
*
|
||||
* getCollections
|
||||
* valid: export
|
||||
|
@ -1135,19 +1132,29 @@ Zotero.Translate.prototype._reportTranslationFailure = function(errorData) {
|
|||
Zotero.Translate.prototype._closeStreams = function() {
|
||||
// serialize RDF and unregister dataSource
|
||||
if(this._rdf) {
|
||||
if(this._rdf.serializer) {
|
||||
this._rdf.serializer.Serialize(this._streams[0]);
|
||||
if(this._streams.length) {
|
||||
// initialize serializer and add prefixes
|
||||
var serializer = Serializer();
|
||||
for(var prefix in this._rdf.namespaces) {
|
||||
serializer.suggestPrefix(prefix, this._rdf.namespaces[prefix]);
|
||||
}
|
||||
|
||||
// serialize in appropriate format
|
||||
if(this.configOptions.dataMode == "rdf/n3") {
|
||||
var output = serializer.statementsToN3(this._rdf.statements);
|
||||
} else {
|
||||
var output = serializer.statementsToXML(this._rdf.statements);
|
||||
}
|
||||
|
||||
// write serialized data to file
|
||||
var intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterOutputStream);
|
||||
intlStream.init(this._streams[0], "UTF-8", 4096, "?".charCodeAt(0));
|
||||
this._streams.push(intlStream);
|
||||
intlStream.writeString(output);
|
||||
}
|
||||
|
||||
try {
|
||||
if(this._rdf.dataSource) {
|
||||
var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"].
|
||||
getService(Components.interfaces.nsIRDFService);
|
||||
rdfService.UnregisterDataSource(this._rdf.dataSource);
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
delete this._rdf.dataSource;
|
||||
delete this._rdf;
|
||||
}
|
||||
|
||||
if(this._streams.length) {
|
||||
|
@ -1247,6 +1254,8 @@ Zotero.Translate.prototype._itemDone = function(item, attachedTo) {
|
|||
}
|
||||
}
|
||||
|
||||
Zotero.debug(item);
|
||||
|
||||
this._itemsDone = true;
|
||||
|
||||
// if we're not supposed to save the item or we're in a child translator,
|
||||
|
@ -1810,147 +1819,153 @@ Zotero.Translate.prototype._importDoneSniffing = function(charset) {
|
|||
/*
|
||||
* set up import for IO
|
||||
*/
|
||||
Zotero.Translate.prototype._importConfigureIO = function(charset) {
|
||||
if(this._storage) {
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "rdf") {
|
||||
this._rdf = new Object();
|
||||
Zotero.Translate.prototype._importConfigureIO = function(charset) {
|
||||
if(this.configOptions.dataMode && (this.configOptions.dataMode == "rdf" || this.configOptions.dataMode == "rdf/n3")) {
|
||||
if(!this._rdf) {
|
||||
Zotero.debug("initializing data store");
|
||||
// initialize data store
|
||||
this._rdf = new Zotero.RDF.AJAW.RDFIndexedFormula();
|
||||
|
||||
// read string out of storage stream
|
||||
Zotero.debug("loading data");
|
||||
// load data into store
|
||||
var IOService = Components.classes['@mozilla.org/network/io-service;1']
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
this._rdf.dataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
|
||||
createInstance(Components.interfaces.nsIRDFDataSource);
|
||||
var parser = Components.classes["@mozilla.org/rdf/xml-parser;1"].
|
||||
createInstance(Components.interfaces.nsIRDFXMLParser);
|
||||
|
||||
// get URI and parse
|
||||
var baseURI = (this.location ? IOService.newURI(this.location, "utf-8", null) : null);
|
||||
parser.parseString(this._rdf.dataSource, baseURI, this._storage);
|
||||
|
||||
// make an instance of the RDF handler
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf.dataSource);
|
||||
} else {
|
||||
this._storageFunctions(true);
|
||||
this._storagePointer = 0;
|
||||
}
|
||||
} else {
|
||||
var me = this;
|
||||
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "rdf") {
|
||||
if(!this._rdf) {
|
||||
this._rdf = new Object()
|
||||
|
||||
var IOService = Components.classes['@mozilla.org/network/io-service;1']
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
if(this._storage) {
|
||||
// parse from string
|
||||
var baseURI = (this.location ? IOService.newURI(this.location, "utf-8", null) : null);
|
||||
var nodeTree = (new DOMParser()).parseFromString(this._storage, 'text/xml');
|
||||
} else {
|
||||
// get URI
|
||||
var fileHandler = IOService.getProtocolHandler("file")
|
||||
.QueryInterface(Components.interfaces.nsIFileProtocolHandler);
|
||||
var URL = fileHandler.getURLSpecFromFile(this.location);
|
||||
var baseURI = fileHandler.getURLSpecFromFile(this.location);
|
||||
|
||||
var RDFService = Components.classes['@mozilla.org/rdf/rdf-service;1']
|
||||
.getService(Components.interfaces.nsIRDFService);
|
||||
this._rdf.dataSource = RDFService.GetDataSourceBlocking(URL);
|
||||
// load XML from file using xmlhttp for charset detection
|
||||
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
xmlhttp.overrideMimeType("text/xml");
|
||||
xmlhttp.open("GET", baseURI, false); // Synchronous
|
||||
xmlhttp.send("");
|
||||
|
||||
// make an instance of the RDF handler
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf.dataSource);
|
||||
var nodeTree = xmlhttp.responseXML;
|
||||
|
||||
Zotero.debug(xmlhttp.responseText)
|
||||
}
|
||||
|
||||
var parser = new Zotero.RDF.AJAW.RDFParser(this._rdf);
|
||||
parser.parse(nodeTree, baseURI);
|
||||
}
|
||||
|
||||
Zotero.debug("adding apis");
|
||||
// add RDF features to sandbox
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf);
|
||||
return;
|
||||
}
|
||||
|
||||
if(this._storage) {
|
||||
// import from string
|
||||
this._storageFunctions(true);
|
||||
this._storagePointer = 0;
|
||||
} else {
|
||||
// import from file
|
||||
|
||||
var me = this;
|
||||
// open file and set read methods
|
||||
if(this._inputStream) {
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsIFileInputStream);
|
||||
} else {
|
||||
// open file and set read methods
|
||||
if(this._inputStream) {
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsIFileInputStream);
|
||||
} else {
|
||||
this._inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
this._inputStream.init(this.location, 0x01, 0664, 0);
|
||||
this._streams.push(this._inputStream);
|
||||
}
|
||||
this._inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
this._inputStream.init(this.location, 0x01, 0664, 0);
|
||||
this._streams.push(this._inputStream);
|
||||
}
|
||||
|
||||
var bomLength = 0;
|
||||
if(charset === undefined || (charset && charset.length > 3 && charset.substr(0, 3) == "UTF")) {
|
||||
// seek past BOM
|
||||
var bomCharset = this._importGetBOM();
|
||||
var bomLength = (bomCharset ? BOMs[bomCharset].length : 0);
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength);
|
||||
if(bomCharset) charset = this._charset = bomCharset;
|
||||
}
|
||||
|
||||
var intlStream = null;
|
||||
if(charset) {
|
||||
// if have detected charset
|
||||
Zotero.debug("Translate: Using detected character set "+charset, 3);
|
||||
// convert from detected charset
|
||||
intlStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
intlStream.init(this._inputStream, charset, 65535,
|
||||
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
me._streams.push(intlStream);
|
||||
}
|
||||
|
||||
// allow translator to set charset
|
||||
this._sandbox.Zotero.setCharacterSet = function(charset) {
|
||||
// seek back to the beginning
|
||||
me._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength);
|
||||
|
||||
var bomLength = 0;
|
||||
if(charset === undefined || (charset && charset.length > 3 && charset.substr(0, 3) == "UTF")) {
|
||||
// seek past BOM
|
||||
var bomCharset = this._importGetBOM();
|
||||
var bomLength = (bomCharset ? BOMs[bomCharset].length : 0);
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength);
|
||||
if(bomCharset) charset = this._charset = bomCharset;
|
||||
}
|
||||
|
||||
var intlStream = null;
|
||||
if(charset) {
|
||||
// if have detected charset
|
||||
Zotero.debug("Translate: Using detected character set "+charset, 3);
|
||||
// convert from detected charset
|
||||
intlStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
intlStream.init(this._inputStream, charset, 65535,
|
||||
intlStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
try {
|
||||
intlStream.init(me._inputStream, charset, 65535,
|
||||
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
me._streams.push(intlStream);
|
||||
} catch(e) {
|
||||
throw "Text encoding not supported";
|
||||
}
|
||||
me._streams.push(intlStream);
|
||||
}
|
||||
|
||||
var str = new Object();
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "line") { // line by line reading
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsILineInputStream);
|
||||
|
||||
// allow translator to set charset
|
||||
this._sandbox.Zotero.setCharacterSet = function(charset) {
|
||||
// seek back to the beginning
|
||||
me._inputStream.QueryInterface(Components.interfaces.nsISeekableStream)
|
||||
.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, bomLength);
|
||||
|
||||
intlStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
try {
|
||||
intlStream.init(me._inputStream, charset, 65535,
|
||||
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
} catch(e) {
|
||||
throw "Text encoding not supported";
|
||||
this._sandbox.Zotero.read = function() {
|
||||
if(intlStream && intlStream instanceof Components.interfaces.nsIUnicharLineInputStream) {
|
||||
var amountRead = intlStream.readLine(str);
|
||||
} else {
|
||||
var amountRead = me._inputStream.readLine(str);
|
||||
}
|
||||
if(amountRead) {
|
||||
return str.value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
me._streams.push(intlStream);
|
||||
}
|
||||
} else { // block reading
|
||||
var sStream;
|
||||
|
||||
var str = new Object();
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "line") { // line by line reading
|
||||
this._inputStream.QueryInterface(Components.interfaces.nsILineInputStream);
|
||||
|
||||
this._sandbox.Zotero.read = function() {
|
||||
if(intlStream && intlStream instanceof Components.interfaces.nsIUnicharLineInputStream) {
|
||||
var amountRead = intlStream.readLine(str);
|
||||
} else {
|
||||
var amountRead = me._inputStream.readLine(str);
|
||||
}
|
||||
this._sandbox.Zotero.read = function(amount) {
|
||||
if(intlStream) {
|
||||
// read from international stream, if one is available
|
||||
var amountRead = intlStream.readString(amount, str);
|
||||
|
||||
if(amountRead) {
|
||||
return str.value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else { // block reading
|
||||
var sStream;
|
||||
|
||||
this._sandbox.Zotero.read = function(amount) {
|
||||
if(intlStream) {
|
||||
// read from international stream, if one is available
|
||||
var amountRead = intlStream.readString(amount, str);
|
||||
|
||||
if(amountRead) {
|
||||
return str.value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// allocate sStream on the fly
|
||||
if(!sStream) {
|
||||
sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
sStream.init(me._inputStream);
|
||||
}
|
||||
|
||||
// read from the scriptable input stream
|
||||
var string = sStream.read(amount);
|
||||
return string;
|
||||
} else {
|
||||
// allocate sStream on the fly
|
||||
if(!sStream) {
|
||||
sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
sStream.init(me._inputStream);
|
||||
}
|
||||
|
||||
// read from the scriptable input stream
|
||||
var string = sStream.read(amount);
|
||||
return string;
|
||||
}
|
||||
|
||||
// attach sStream to stack of streams to close
|
||||
this._streams.push(sStream);
|
||||
}
|
||||
|
||||
// attach sStream to stack of streams to close
|
||||
this._streams.push(sStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2098,20 +2113,12 @@ Zotero.Translate.prototype._exportConfigureIO = function() {
|
|||
// attach to stack of streams to close at the end
|
||||
this._streams.push(fStream);
|
||||
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "rdf") { // rdf io
|
||||
this._rdf = new Object();
|
||||
if(this.configOptions.dataMode && (this.configOptions.dataMode == "rdf" || this.configOptions.dataMode == "rdf/n3")) { // rdf io
|
||||
// initialize data store
|
||||
this._rdf = new Zotero.RDF.AJAW.RDFIndexedFormula();
|
||||
|
||||
// create data source
|
||||
this._rdf.dataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=xml-datasource"].
|
||||
createInstance(Components.interfaces.nsIRDFDataSource);
|
||||
// create serializer
|
||||
this._rdf.serializer = Components.classes["@mozilla.org/rdf/xml-serializer;1"].
|
||||
createInstance(Components.interfaces.nsIRDFXMLSerializer);
|
||||
this._rdf.serializer.init(this._rdf.dataSource);
|
||||
this._rdf.serializer.QueryInterface(Components.interfaces.nsIRDFXMLSource);
|
||||
|
||||
// make an instance of the RDF handler
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf.dataSource, this._rdf.serializer);
|
||||
// add RDF features to sandbox
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf);
|
||||
} else {
|
||||
// regular io; write just writes to file
|
||||
var intlStream = null;
|
||||
|
@ -2331,14 +2338,12 @@ Zotero.Translate.prototype._exportGetCollection = function() {
|
|||
*/
|
||||
Zotero.Translate.prototype._initializeInternalIO = function() {
|
||||
if(this.type == "import" || this.type == "export") {
|
||||
if(this.configOptions.dataMode && this.configOptions.dataMode == "rdf") {
|
||||
this._rdf = new Object();
|
||||
// use an in-memory data source for internal IO
|
||||
this._rdf.dataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
|
||||
createInstance(Components.interfaces.nsIRDFDataSource);
|
||||
if(this.configOptions.dataMode && (this.configOptions.dataMode == "rdf" || this.configOptions.dataMode == "rdf/n3")) {
|
||||
// initialize data store
|
||||
this._rdf = new Zotero.RDF.AJAW.RDFIndexedFormula();
|
||||
|
||||
// make an instance of the RDF handler
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf.dataSource);
|
||||
// add RDF features to sandbox
|
||||
this._sandbox.Zotero.RDF = new Zotero.Translate.RDF(this._rdf);
|
||||
} else {
|
||||
this._storage = "";
|
||||
this._storageLength = 0;
|
||||
|
@ -2648,207 +2653,132 @@ Zotero.Translate.GenerateZoteroCollectionClass = function() {
|
|||
*
|
||||
* If an import/export translator specifies dataMode RDF, this is the interface,
|
||||
* accessible from model.
|
||||
*
|
||||
* In order to simplify things, all classes take in their resource/container
|
||||
* as either the Mozilla native type or a string, but all
|
||||
* return resource/containers as Mozilla native types (use model.toString to
|
||||
* convert)
|
||||
*/
|
||||
|
||||
Zotero.Translate.RDF = function(dataSource, serializer) {
|
||||
this._RDFService = Components.classes['@mozilla.org/rdf/rdf-service;1']
|
||||
.getService(Components.interfaces.nsIRDFService);
|
||||
this._AtomService = Components.classes["@mozilla.org/atom-service;1"]
|
||||
.getService(Components.interfaces.nsIAtomService);
|
||||
this._RDFContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
|
||||
.getService(Components.interfaces.nsIRDFContainerUtils);
|
||||
|
||||
this._dataSource = dataSource;
|
||||
this._serializer = serializer;
|
||||
}
|
||||
|
||||
// turn an nsISimpleEnumerator into an array
|
||||
Zotero.Translate.RDF.prototype._deEnumerate = function(enumerator) {
|
||||
if(!(enumerator instanceof Components.interfaces.nsISimpleEnumerator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var resources = new Array();
|
||||
|
||||
while(enumerator.hasMoreElements()) {
|
||||
var resource = enumerator.getNext();
|
||||
try {
|
||||
resource.QueryInterface(Components.interfaces.nsIRDFLiteral);
|
||||
resources.push(resource.Value);
|
||||
} catch(e) {
|
||||
resource.QueryInterface(Components.interfaces.nsIRDFResource);
|
||||
resources.push(resource);
|
||||
}
|
||||
}
|
||||
|
||||
if(resources.length) {
|
||||
return resources;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
Zotero.Translate.RDF = function(dataStore) {
|
||||
this._dataStore = dataStore;
|
||||
this._containerCounts = {};
|
||||
}
|
||||
|
||||
// get a resource as an nsIRDFResource, instead of a string
|
||||
Zotero.Translate.RDF.prototype._getResource = function(about) {
|
||||
try {
|
||||
if(!(about instanceof Components.interfaces.nsIRDFResource)) {
|
||||
about = this._RDFService.GetResource(about);
|
||||
}
|
||||
} catch(e) {
|
||||
throw("Zotero.Translate.RDF: Invalid RDF resource: "+about);
|
||||
}
|
||||
return about;
|
||||
return (typeof about == "object" ? about : new Zotero.RDF.AJAW.RDFSymbol(about));
|
||||
}
|
||||
|
||||
|
||||
// USED FOR OUTPUT
|
||||
|
||||
// writes an RDF triple
|
||||
Zotero.Translate.RDF.prototype.addStatement = function(about, relation, value, literal) {
|
||||
about = this._getResource(about);
|
||||
|
||||
if(!(value instanceof Components.interfaces.nsIRDFResource)) {
|
||||
if(literal) {
|
||||
// zap chars that Mozilla will mangle
|
||||
if(typeof(value) == "string") {
|
||||
value = value.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
||||
}
|
||||
|
||||
try {
|
||||
value = this._RDFService.GetLiteral(value);
|
||||
} catch(e) {
|
||||
throw "Zotero.Translate.RDF.addStatement: Could not convert to literal";
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
value = this._RDFService.GetResource(value);
|
||||
} catch(e) {
|
||||
throw "Zotero.Translate.RDF.addStatement: Could not convert to resource";
|
||||
}
|
||||
}
|
||||
if(literal) {
|
||||
// zap chars that Mozilla will mangle
|
||||
value = value.toString().replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
||||
} else {
|
||||
value = this._getResource(value);
|
||||
}
|
||||
|
||||
this._dataSource.Assert(about, this._RDFService.GetResource(relation), value, true);
|
||||
this._dataStore.add(this._getResource(about), this._getResource(relation), value);
|
||||
}
|
||||
|
||||
// creates an anonymous resource
|
||||
Zotero.Translate.RDF.prototype.newResource = function() {
|
||||
return this._RDFService.GetAnonymousResource()
|
||||
return new Zotero.RDF.AJAW.RDFBlankNode();
|
||||
};
|
||||
|
||||
// creates a new container
|
||||
Zotero.Translate.RDF.prototype.newContainer = function(type, about) {
|
||||
about = this._getResource(about);
|
||||
const rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
||||
const containerTypes = {"bag":"Bag", "seq":"Seq", "alt":"Alt"};
|
||||
|
||||
type = type.toLowerCase();
|
||||
if(type == "bag") {
|
||||
return this._RDFContainerUtils.MakeBag(this._dataSource, about);
|
||||
} else if(type == "seq") {
|
||||
return this._RDFContainerUtils.MakeSeq(this._dataSource, about);
|
||||
} else if(type == "alt") {
|
||||
return this._RDFContainerUtils.MakeAlt(this._dataSource, about);
|
||||
} else {
|
||||
throw "Invalid container type in model.newContainer";
|
||||
}
|
||||
}
|
||||
|
||||
// adds a new container element (index optional)
|
||||
Zotero.Translate.RDF.prototype.addContainerElement = function(about, element, literal, index) {
|
||||
if(!(about instanceof Components.interfaces.nsIRDFContainer)) {
|
||||
about = this._getResource(about);
|
||||
var container = Components.classes["@mozilla.org/rdf/container;1"].
|
||||
createInstance(Components.interfaces.nsIRDFContainer);
|
||||
container.Init(this._dataSource, about);
|
||||
about = container;
|
||||
}
|
||||
if(!(element instanceof Components.interfaces.nsIRDFResource)) {
|
||||
if(literal) {
|
||||
element = this._RDFService.GetLiteral(element);
|
||||
} else {
|
||||
element = this._RDFService.GetResource(element);
|
||||
}
|
||||
if(!containerTypes[type]) {
|
||||
throw "Invalid container type in Zotero.RDF.newContainer";
|
||||
}
|
||||
|
||||
if(index) {
|
||||
about.InsertElementAt(element, index, true);
|
||||
} else {
|
||||
about.AppendElement(element);
|
||||
}
|
||||
var about = this._getResource(about);
|
||||
this.addStatement(about, rdf+"type", rdf+containerTypes[type], false);
|
||||
this._containerCounts[about.toNT()] = 1;
|
||||
|
||||
return about;
|
||||
}
|
||||
|
||||
// adds a new container element
|
||||
Zotero.Translate.RDF.prototype.addContainerElement = function(about, element, literal) {
|
||||
const rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
||||
|
||||
var about = this._getResource(about);
|
||||
this._dataStore.add(about, new Zotero.RDF.AJAW.RDFSymbol(rdf+"_"+(this._containerCounts[about.toNT()]++)), element, literal);
|
||||
}
|
||||
|
||||
// gets container elements as an array
|
||||
Zotero.Translate.RDF.prototype.getContainerElements = function(about) {
|
||||
if(!(about instanceof Components.interfaces.nsIRDFContainer)) {
|
||||
about = this._getResource(about);
|
||||
var container = Components.classes["@mozilla.org/rdf/container;1"].
|
||||
createInstance(Components.interfaces.nsIRDFContainer);
|
||||
container.Init(this._dataSource, about);
|
||||
about = container;
|
||||
const liPrefix = "http://www.w3.org/1999/02/22-rdf-syntax-ns#_";
|
||||
|
||||
var about = this._getResource(about);
|
||||
var statements = this._dataStore.statementsMatching(about);
|
||||
var containerElements = [];
|
||||
|
||||
// loop over arcs out looking for list items
|
||||
for each(var statement in statements) {
|
||||
if(statement.predicate.uri.substr(0, liPrefix.length) == liPrefix) {
|
||||
var number = statement.predicate.uri.substr(liPrefix.length);
|
||||
|
||||
// make sure these are actually numeric list items
|
||||
var intNumber = parseInt(number);
|
||||
if(number == intNumber.toString()) {
|
||||
// add to element array
|
||||
containerElements[intNumber-1] = (statement.object.termType == "literal" ? statement.object.toString() : statement.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._deEnumerate(about.GetElements());
|
||||
return containerElements;
|
||||
}
|
||||
|
||||
// sets a namespace
|
||||
Zotero.Translate.RDF.prototype.addNamespace = function(prefix, uri) {
|
||||
if(this._serializer) { // silently fail, in case the reason the scraper
|
||||
// is failing is that we're using internal IO
|
||||
this._serializer.addNameSpace(this._AtomService.getAtom(prefix), uri);
|
||||
}
|
||||
this._dataStore.setPrefixForURI(prefix, uri);
|
||||
}
|
||||
|
||||
// gets a resource's URI
|
||||
Zotero.Translate.RDF.prototype.getResourceURI = function(resource) {
|
||||
if(typeof(resource) == "string") {
|
||||
return resource;
|
||||
}
|
||||
|
||||
resource.QueryInterface(Components.interfaces.nsIRDFResource);
|
||||
return resource.ValueUTF8;
|
||||
if(typeof(resource) == "string") return resource;
|
||||
if(resource.uri) return resource.uri;
|
||||
if(resource.toNT == undefined) throw "Zotero.RDF: getResourceURI called on invalid resource";
|
||||
return resource.toNT();
|
||||
}
|
||||
|
||||
// USED FOR INPUT
|
||||
|
||||
// gets all RDF resources
|
||||
Zotero.Translate.RDF.prototype.getAllResources = function() {
|
||||
var resourceEnumerator = this._dataSource.GetAllResources();
|
||||
return this._deEnumerate(resourceEnumerator);
|
||||
return [s[0].subject for each(s in this._dataStore.subjectIndex)];
|
||||
}
|
||||
|
||||
// gets arcs going in
|
||||
Zotero.Translate.RDF.prototype.getArcsIn = function(resource) {
|
||||
resource = this._getResource(resource);
|
||||
|
||||
var arcEnumerator = this._dataSource.ArcLabelsIn(resource);
|
||||
return this._deEnumerate(arcEnumerator);
|
||||
var statements = this._dataStore.objectIndex[this._dataStore.canon(this._getResource(resource))];
|
||||
if(!statements) return false;
|
||||
return [s.predicate for each(s in statements)];
|
||||
}
|
||||
|
||||
// gets arcs going out
|
||||
Zotero.Translate.RDF.prototype.getArcsOut = function(resource) {
|
||||
resource = this._getResource(resource);
|
||||
|
||||
var arcEnumerator = this._dataSource.ArcLabelsOut(resource);
|
||||
return this._deEnumerate(arcEnumerator);
|
||||
var statements = this._dataStore.subjectIndex[this._dataStore.canon(this._getResource(resource))];
|
||||
if(!statements) return false;
|
||||
return [s.predicate for each(s in statements)];
|
||||
}
|
||||
|
||||
// gets source resources
|
||||
Zotero.Translate.RDF.prototype.getSources = function(resource, property) {
|
||||
property = this._getResource(property);
|
||||
resource = this._getResource(resource);
|
||||
|
||||
var enumerator = this._dataSource.GetSources(property, resource, true);
|
||||
return this._deEnumerate(enumerator);
|
||||
var statements = this._dataStore.statementsMatching(undefined, this._getResource(property), this._getResource(resource));
|
||||
if(!statements.length) return false;
|
||||
return [s.subject for each(s in statements)];
|
||||
}
|
||||
|
||||
// gets target resources
|
||||
Zotero.Translate.RDF.prototype.getTargets = function(resource, property) {
|
||||
property = this._getResource(property);
|
||||
resource = this._getResource(resource);
|
||||
|
||||
var enumerator = this._dataSource.GetTargets(resource, property, true);
|
||||
return this._deEnumerate(enumerator);
|
||||
var statements = this._dataStore.statementsMatching(this._getResource(resource), this._getResource(property));
|
||||
if(!statements.length) return false;
|
||||
return [(s.object.termType == "literal" ? s.object.toString() : s.object) for each(s in statements)];
|
||||
}
|
|
@ -73,6 +73,28 @@ for (var i=0; i<xpcomFiles.length; i++) {
|
|||
.loadSubScript("chrome://zotero/content/xpcom/" + xpcomFiles[i] + ".js");
|
||||
}
|
||||
|
||||
|
||||
// Load RDF files into Zotero.RDF.AJAW namespace (easier than modifying all of the references)
|
||||
var rdfXpcomFiles = [
|
||||
'rdf/uri',
|
||||
'rdf/term',
|
||||
'rdf/identity',
|
||||
'rdf/match',
|
||||
'rdf/n3parser',
|
||||
'rdf/rdfparser',
|
||||
'rdf/serialize',
|
||||
'rdf'
|
||||
];
|
||||
|
||||
Zotero.RDF = {AJAW:{}};
|
||||
|
||||
for (var i=0; i<rdfXpcomFiles.length; i++) {
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://zotero/content/xpcom/" + rdfXpcomFiles[i] + ".js", Zotero.RDF.AJAW);
|
||||
}
|
||||
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://global/content/nsTransferable.js");
|
||||
|
|
Loading…
Add table
Reference in a new issue