Created
November 22, 2008 15:21
-
-
Save snj14/27851 to your computer and use it in GitHub Desktop.
RDF Query library for firefox extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is library for RDF | |
// usage: | |
// | |
// var rdf = RDF("chrome://hoge/content/test.rdf"); // note: this is synchronous. not asynchronous. | |
// | |
// rdf.getAsXML() // => "<xml>....</xml>" | |
// | |
// rdf.query() | |
// .prefix({ | |
// rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |
// , rdfs: "http://www.w3.org/2000/01/rdf-schema#" | |
// , ex: "http://example.org/term#" | |
// }) | |
// .where('<http://example.org/> ex:x ?x') | |
// .where('?x ex:foo ?foo') | |
// .where('?x ex:bar ?bar') | |
// .where('?x ?baz "baz"') | |
// .select('?foo ?bar ?baz'); | |
// res // => [[foo1, foo2, foo3], [bar1, bar2], [baz1]] | |
// | |
// rdf.query() | |
// .where('<http://example.org/> ex:x ?x') | |
// .add('?x ex:foo "foooooo!"') | |
// .add("?x ex:foo 'Foooooo!'") | |
// .add("?x ex:foo \"foofoofoo\"") | |
// | |
// // by using XHR.js | |
// XHR({ | |
// url:'http://example.org/example.rdf' | |
// onload:function(response){ | |
// var rdf = RDF(response.responseXML); // note: responseXML is document object | |
// rdf.query() | |
// .prefix('') | |
// .where('') | |
// .where('') | |
// .select(''); | |
// } | |
// }); | |
// todo: | |
// | |
// delete triple query | |
// | |
function RDF(aValue){ // url(string) or xml or document | |
if(typeof Cc == 'undefined') const Cc = Components.classes; | |
if(typeof Ci == 'undefined') const Ci = Components.interfaces; | |
var RDFService = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); | |
// [aSource] --[aProperty]--> [aTarget] // mozilla | |
// [aSubject] --[aPredicate]--> [aObject] // this library | |
function RDFWrapper(){return (this instanceof RDFWrapper) ? this : new RDFWrapper()}; | |
RDFWrapper.FromURL = function(aURL){ | |
var ds = RDFService.GetDataSourceBlocking(aURL); | |
[Ci.nsIRDFDataSource, Ci.nsIRDFRemoteDataSource, Ci.nsIRDFXMLSink].forEach(function(ifc){ | |
if(ds instanceof ifc); | |
}); | |
ds.Refresh(false); // to enable dataSource.Assert() but why??? | |
return new RDFWrapper().init({dataSource:ds}); | |
} | |
RDFWrapper.FromDocument = function(aXMLDocument){ | |
var aRDFString = (((typeof aXMLDocument) == 'xml') ? ("<?xml version=\"1.0\"?>\n" + aXMLDocument.toString()) : | |
(new XMLSerializer()).serializeToString(aXMLDocument) | |
); | |
var rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].createInstance(Ci.nsIRDFXMLParser); | |
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
var aBaseURI = ios.newURI(((typeof aXMLDocument == 'xml') ? | |
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" : // is this ok? | |
aXMLDocument.baseURI), | |
null, null); | |
var aDataSource = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].createInstance(Ci.nsIRDFDataSource); | |
var res = rdfParser.parseString(aDataSource, aBaseURI, aRDFString); | |
[Components.interfaces.nsIRDFDataSource, | |
Components.interfaces.nsIRDFRemoteDataSource, | |
Components.interfaces.nsIRDFXMLSink].forEach(function(ifc){ | |
if(aDataSource instanceof ifc); | |
}); | |
return new RDFWrapper().init({dataSource:aDataSource}); | |
} | |
RDFWrapper.prototype = { | |
init: function(opts){ | |
this.dataSource = opts.dataSource; | |
return this; | |
} | |
, query: function(){return RDFQuery().init(this, this.dataSource)} | |
, filterEnumerator: function(enumerator, fn){ | |
var arr = []; | |
while(enumerator.hasMoreElements()){ | |
var item = enumerator.getNext(); | |
if(item) [Ci.nsIRDFResource, Ci.nsIRDFLiteral].forEach(function(ifc){if((item instanceof ifc));}); | |
if(item && (item = fn(item)) && item && item.length){ | |
if(item[0] instanceof Ci.nsIRDFResource){ | |
arr.push(item); | |
}else{// fn return Array item when call filterEnumerator recursively | |
item.forEach(function(i){ | |
arr.push(i) | |
}) | |
} | |
} | |
} | |
return arr; | |
} | |
// [] -> AnonymousResource | |
// URI -> Resource | |
// Number -> IntLiteral | |
// other -> Literal | |
// null -> same as unspecified | |
, getResource: function(aValue){ | |
if((aValue instanceof Array) && (aValue.length == 0)) return null; | |
return (((aValue instanceof Ci.nsIRDFResource) && aValue) || | |
((typeof aValue == 'number') && RDFService.GetIntLiteral(aValue)) || | |
((typeof aValue == 'string') && aValue.match(/[a-zA-Z]+:(\/\/)?.+/) && RDFService.GetResource(aValue)) || | |
((typeof aValue == 'string') && RDFService.GetLiteral(aValue)) || | |
(((aValue == null) || (typeof aValue == 'undefined')) && undefined) | |
) | |
} | |
, getTriples: function(object){// aSubject, aPredicate, aObject | |
var self = this; | |
var aSubject, aPredicate, aObject; // they are Resource | |
[aSubject, aPredicate, aObject] = (Array.slice(arguments)).map(function(e){return self.getResource(e)}); | |
// if aObject === null is true, aObject is specified anonymous resource([]) but it may not exist | |
var sa = (aSubject === null); // aSubject is Anonymous resource | |
var pa = (aPredicate === null); | |
var oa = (aObject === null); | |
var triples = ((aSubject && aPredicate) ? this.getTriplesFromSP(aSubject, aPredicate, oa) : | |
(aPredicate && aObject) ? this.getTriplesFromPO(sa, aPredicate, aObject) : | |
(aSubject && aObject) ? this.getTriplesFromSO(aSubject, pa, aObject) : | |
aSubject ? this.getTriplesFromS(aSubject, pa, oa) : | |
aObject ? this.getTriplesFromO(sa, pa, aObject) : | |
aPredicate ? this.getTriplesFromP(sa, aPredicate, oa) : | |
(sa || pa || oa) ? this.getTriplesFromAnonymous(sa, pa, oa) : | |
[]) | |
return triples | |
} | |
, getTriplesFromSP: function(aSubject, aPredicate, aObjectIsUnknownAnonymous){ | |
var self = this; | |
return self.filterEnumerator(self.getObjects(aSubject, aPredicate), function(aObject){ | |
if(aObjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aObject)) return []; | |
return [aSubject, aPredicate, aObject] | |
}); | |
} | |
, getTriplesFromPO: function(aSubjectIsUnknownAnonymous, aPredicate, aObject){ | |
var self = this; | |
return self.filterEnumerator(self.getSubjects(aPredicate, aObject), function(aSubject){ | |
if(aSubjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aSubject)) return []; | |
return [aSubject, aPredicate, aObject] | |
}) | |
} | |
, getTriplesFromSO: function(aSubject, aPredicateIsUnknownAnonymous, aObject){ | |
var self = this; | |
return self.filterEnumerator(self.getPredicatesFromSubject(aSubject), function(aPredicate){ | |
if(aPredicateIsUnknownAnonymous && !RDFService.IsAnonymousResource(aPredicate)) return []; | |
return self.filterEnumerator(self.getObjects(aSubject, aPredicate), function(aFoundObject){ | |
if(aFoundObject.Value != aObject.Value) return []; | |
return [aSubject, aPredicate ,aObject] | |
}) | |
}) | |
} | |
, getTriplesFromS: function(aSubject, aPredicateIsUnknownAnonymous, aObjectIsUnknownAnonymous){ | |
var self = this; | |
return self.filterEnumerator(self.getPredicatesFromSubject(aSubject), function(aPredicate){ | |
if(aPredicateIsUnknownAnonymous && !RDFService.IsAnonymousResource(aPredicate)) return []; | |
return self.filterEnumerator(self.getObjects(aSubject, aPredicate), function(aObject){ | |
if(aObjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aObject)) return []; | |
return [aSubject, aPredicate, aObject] | |
}) | |
}) | |
} | |
, getTriplesFromO: function(aSubjectIsUnknownAnonymous, aObject, aPredicateIsUnknownAnonymous){ | |
var self = this; | |
return self.filterEnumerator(self.getPredicatesFromObject(aObject), function(aPredicate){ | |
if(aPredicateIsUnknownAnonymous && !RDFService.IsAnonymousResource(aPredicate)) return []; | |
return self.filterEnumerator(self.getSubjects(aPredicate, aObject), function(aSubject){ | |
if(aSubjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aSubject)) return []; | |
return [aSubject, aPredicate, aObject] | |
}) | |
}) | |
} | |
, getTriplesFromP: function(aSubjectIsUnknownAnonymous, aPredicate, aObjectIsUnknownAnonymous){ | |
var self = this; | |
return self.filterEnumerator(self.getAllResources(), function(aSubject){ | |
if(aSubjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aSubject)) return []; | |
return self.filterEnumerator(self.getPredicatesFromSubject(aSubject), function(aFoundPredicate){ | |
if(aFoundPredicate.Value != aPredicate.Value) return []; | |
return self.filterEnumerator(self.getObjects(aSubject, aPredicate), function(aObject){ | |
if(aObjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aObject)) return []; | |
return [aSubject, aFoundPredicate, aObject] | |
}) | |
}) | |
}) | |
} | |
, getTriplesFromAnonymous: function(aSubjectIsUnknownAnonymous, aPredicateIsUnknownAnonymous, aObjectIsUnknownAnonymous){ | |
var self = this; | |
return self.filterEnumerator(self.getAllResources(), function(aSubject){ | |
if(aSubjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aSubject)) return []; | |
return self.filterEnumerator(self.getPredicatesFromSubject(aSubject), function(aPredicate){ | |
if(aPredicateIsUnknownAnonymous && !RDFService.IsAnonymousResource(aPredicate)) return []; | |
return self.filterEnumerator(self.getObjects(aSubject, aPredicate), function(aObject){ | |
if(aObjectIsUnknownAnonymous && !RDFService.IsAnonymousResource(aObject)) return []; | |
return [aSubject, aPredicate, aObject] | |
}) | |
}) | |
}) | |
} | |
, setTriple: function(aSubject, aPredicate, aObject){ | |
var self = this; | |
[aSubject, aPredicate, aObject] = [aSubject, aPredicate, aObject].map(function(e){return self.getResource(e)}); | |
var added = !this.hasTriple(aSubject, aPredicate, aObject); | |
this.assertTriple(aSubject, aPredicate, aObject); | |
return added; | |
} | |
// not yet | |
, saveAsXML: function(){ | |
var onload = function(){ | |
log(outputStream.data) | |
} | |
this.serializeToStream({onwrite:onwrite}); | |
} | |
, getAsXML: function(){ | |
return this.asXML(); | |
} | |
// mozilla interface | |
, getSubjects: function(aPredicate, aObject){return this.dataSource.GetSources(aPredicate, aObject, true)} | |
, getPredicatesFromSubject: function(aSubject){return this.dataSource.ArcLabelsOut(aSubject)} | |
, getPredicatesFromObject: function(aObject){return this.dataSource.ArcLabelsIn(aObject)} | |
, getObjects: function(aSubject, aPredicate){return this.dataSource.GetTargets(aSubject, aPredicate, true)} | |
, getAllResources: function(){return this.dataSource.GetAllResources()} | |
, modifySubject: function(){return this.dataSource.Move(aOldSubject, aNewSubject, aPredicate, aOldSource)} | |
, modifyObject: function(){return this.dataSource.Change(aSubject, aPredicate, aOldObject, aNewObject)} | |
, assertTriple: function(aSubject, aPredicate, aObject){return this.dataSource.Assert(aSubject, aPredicate, aObject, true)} | |
, removeTriple: function(aSubject, aPredicate, aObject){return this.dataSource.Unassert(aSubject, aPredicate, aObject)} | |
, hasTriple: function(aSubject, aPredicate, aObject){return this.dataSource.HasAssertion(aSubject, aPredicate, aObject, true)} | |
, hasSP: function(aSubject, aPredicate){return this.dataSource.hasArcIn(aSubject, aPredicate)} | |
, hasPO: function(aPredicate, aObject){return this.dataSource.hasArcOut(aObject, aPredicate)} | |
, asXML: function(onWrite){ | |
var outputStream = { | |
data: "", | |
close : function(){}, | |
flush : function(){}, | |
write : function (buffer,count){ | |
onWrite ? onWrite(buffer) : this.data += buffer; | |
return count; | |
}, | |
writeFrom : function (stream,count){}, | |
isNonBlocking: false | |
} | |
var serialize = Cc["@mozilla.org/rdf/xml-serializer;1"].createInstance(Ci.nsIRDFXMLSerializer); | |
serialize.init(this.dataSource); | |
serialize.QueryInterface(Ci.nsIRDFXMLSource).Serialize(outputStream); | |
return (onWrite ? null : outputStream.data); | |
} | |
}; | |
function RDFQuery(){ return (this instanceof RDFQuery) ? this : new RDFQuery()}; | |
RDFQuery.prototype = { | |
init: function(aRDF, aDataSource){ | |
this.rdf = aRDF; | |
this.dataSource = aDataSource; | |
this.namespace = {}; // {'rdf:' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', } | |
this.variable = {}; // {'foo' : [RDFResource1, RDFResource2 ,,,], } | |
this.queryTripleMap = []; // [{ qurey: [null, null, "foo"] // null is not variable | |
// , triples: [triple1,triple2] | |
// }, olderOjbect,,, ] | |
return this; | |
} | |
, parseIRI: function(aIRI){ // <IRI> => IRI | |
return (aIRI.match(/^<.+>$/) && aIRI.slice(1,-1)) | |
} | |
, parsePrefixedIRI: function(aIRI){ // "rdf:foo" => [http://www.w3.org/1999/02/22-rdf-syntax-ns#foo] | |
var res = aIRI.match(/^(\w.*:)([a-zA-Z]+)/); | |
if(!res) return false; | |
var prefix = res[1]; | |
var name = res[2]; | |
var uri = this.namespace[prefix]; | |
if(!uri) throw(Error("RDFQuery: specified prefix [" + prefix + "] is not declared.")); | |
return uri + name; | |
} | |
, parseVariable: function(aVariable){ // ?foo or $foo. they are same variables. | |
var matched = aVariable.match(/^($|\?)(.+)/); | |
if(!matched) return false; | |
var aVariableName = matched[2]; | |
return {name: aVariableName, | |
resources: this.variable[aVariableName]} | |
} | |
, parseanonymous: function(str){ | |
return (str == "[]") ? str : false; | |
} | |
, parseStringLiteral: function(aString){ | |
return ((aString.slice(0,1) == '"') && (aString.slice(-1) == '"')) ? aString.slice(1,-1) : false; | |
} | |
, parseIntLiteral: function(aInteger){ | |
return aInteger.match('^[0-9]+$') ? parseInt(aInteger) : false; | |
} | |
, optimizeVariables: function(){// 後から追加された条件による変数の最適化を行わないと変数が指すリソースが増えまくる | |
var self = this; | |
this.queryTripleMap.map(function(obj){ | |
obj.query.forEach(function(aVariableName,n){ // just 3 times | |
if(aVariableName){ | |
if(self.variable[aVariableName].length > 0){ | |
var aNewObjTriples = []; | |
self.variable[aVariableName] = self.variable[aVariableName].filter(function(aStragedResource){ | |
return obj.triples.some(function(aTriple){ | |
return ((aTriple[n] == aStragedResource) && aNewObjTriples.push(aTriple) && true) | |
}) | |
}) | |
obj.triples = aNewObjTriples; | |
}else{ | |
obj.triples.forEach(function(aTriple){ | |
if(!self.variable[aVariableName].some(function(aStragedTriple){ | |
return aStragedTriple == aTriple[n] | |
})){ | |
self.variable[aVariableName].push(aTriple[n]); | |
} | |
}) | |
} | |
} | |
}) | |
}) | |
} | |
, splitQueryTriple: function(aQuery){ | |
var quoted = new RegExp('\\s*((["\'])([^\\2]*)\\2)\\s*'); | |
var whitespace = new RegExp('\\s+'); | |
var remained = aQuery.replace(quoted, ""); | |
var aQuotedString = RegExp.$1 | |
var res = remained.split(whitespace); | |
if((remained != aQuery) && aQuotedString){ | |
res.push(aQuotedString); | |
} | |
// log('splitQueryTriple:',[aQuery, aQuotedString]) | |
return res; | |
} | |
// interface for using RDFQuery | |
, prefix: function(aNamespace){this.namespace = aNamespace; return this} | |
, where: function(aQuery){ | |
var self = this; | |
// aSubject, aPredicate, aObject | |
var resources = [null, null, null]; | |
var params = [null, null, null]; | |
var variable = [null, null, null]; // for fist time variable | |
var queries = this.splitQueryTriple(aQuery); | |
queries.forEach(function(aValue,n){ | |
var anonymous; | |
var aInteger,aString,aIRI; | |
if(typeof(aInteger = self.parseIntLiteral(aValue)) == 'number'){ | |
params[n] = aInteger; | |
}else if((aString = self.parseStringLiteral(aValue))){ | |
params[n] = aString; | |
}else if((aIRI = self.parseIRI(aValue) || self.parsePrefixedIRI(aValue))){ | |
params[n] = aIRI; | |
}else if((anonymous = self.parseanonymous(aValue))){ | |
params[n] = []; | |
}else{ // variable | |
var res = self.parseVariable(aValue); | |
variable[n] = res.name; | |
if(!res.resources){ | |
self.variable[res.name] = []; | |
}else if(res.resources.length == 0){ | |
throw(Error("RDFQuery: specified variable [" + res.name + "] is pointing empty resources list.")); | |
}else if(res.resources.length == 1){ | |
params[n] = res.resources | |
}else if(res.resources.length > 1){ | |
params[n] = null; | |
resources[n] = res.resources | |
} | |
} | |
}) | |
var triples = this.rdf.getTriples.apply(this.rdf, params); | |
resources.forEach(function(resourceList, n){ | |
if(resourceList){ | |
triples = triples.filter(function(aTriple){ | |
return resourceList.filter(function(resource){ | |
return aTriple[n] == resource | |
}) | |
}) | |
} | |
}) | |
this.queryTripleMap.unshift({ | |
query: variable | |
, 'triples': triples | |
}); | |
this.optimizeVariables(); | |
return this | |
} | |
, select: function(aQuery){ | |
var self = this; | |
var queries = aQuery.split(" "); | |
var result = []; | |
queries.forEach(function(query){ | |
var obj = self.parseVariable(query); | |
if(!obj.resources) throw(Error("RDFQuery: specified variable [" + obj.name + "] is not declared.")); | |
if(obj.resources.length){ | |
result.push(obj.resources) | |
} | |
}) | |
return result; | |
} | |
, add: function(aQuery){ | |
var self = this; | |
var queries = this.splitQueryTriple(aQuery); | |
var params = [[],[],[]]; // subjects, predicates, objects | |
queries.forEach(function(aValue,n){ | |
var anonymous; | |
var aInteger,aString,aIRI; | |
if(typeof(aInteger = self.parseIntLiteral(aValue)) == 'number'){ | |
params[n].push(aInteger); | |
}else if((aString = self.parseStringLiteral(aValue))){ | |
params[n].push(aString); | |
}else if((aIRI = self.parseIRI(aValue) || self.parsePrefixedIRI(aValue))){ | |
params[n].push(aIRI); | |
}else if((anonymous = self.parseanonymous(aValue))){ | |
params[n].push([]); | |
}else{ // variable | |
var res = self.parseVariable(aValue); | |
if(!res.resources){ | |
throw(Error("RDFQuery: specified variable [" + res.name + "] is not declared.")); | |
}else if(res.resources.length == 0){ | |
throw(Error("RDFQuery: specified variable [" + res.name + "] is pointing empty resources list.")) | |
}else{ | |
params[n] = res.resources | |
} | |
} | |
}) | |
params[0].forEach(function(aSubject){ | |
params[1].forEach(function(aPredicate){ | |
params[2].forEach(function(aObject){ | |
self.rdf.setTriple(aSubject, aPredicate, aObject) | |
}) | |
}) | |
}) | |
return this; | |
} | |
}; | |
// is this ok ? | |
return (((typeof(aValue) == 'string') && ~aValue.indexOf(":")) ? RDFWrapper.FromURL(aValue) : | |
(typeof(aValue) == 'xml') ? RDFWrapper.FromDocument(aValue) : | |
(aValue instanceof Document) ? RDFWrapper.FromDocument(aValue) : | |
false); | |
} | |
RDF.Test = function(){ | |
var xml = <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |
xmlns:a="http://example.org/a/" | |
xmlns:b="http://example.org/b#" | |
xmlns:c="http://example.org/c/c"> | |
<rdf:Description rdf:about="http://example.org/"> | |
<a:foo>foo text</a:foo> | |
<b:bar>bar text</b:bar> | |
<c:baz>baz text</c:baz> | |
</rdf:Description> | |
</rdf:RDF>; | |
// var rdf = RDF("http://test.test/test001.rdf"); | |
var rdf = RDF(xml); | |
log(rdf.asXML()); | |
rdf.query() | |
.prefix({ | |
"d:":"http://example.org/ns1#" | |
}) | |
.add('<http://example.org/> d:qux "this is qux"'); // 破壊的 | |
log(rdf.asXML()); | |
var res = rdf.query() | |
.prefix({ | |
"a:":"http://www.w3.org/TR/REC-rdf-syntax#" | |
, "b:":"http://www.w3.org/TR/REC-rdf-syntax-blah-blah#" | |
, "c:":"http://www.w3.org/TR/REC-rdf-syntaxc#" | |
, "d:":"http://example.org/ns1#" | |
}) | |
.where('<http://example.org/> a:foo ?foo') | |
.where('?uri a:foo ?foo') | |
.where('?uri b:bar ?bar') | |
.where('?uri1 b:bar ?bar') | |
.where('?uri1 c:baz ?baz') | |
.where('?uri1 d:qux ?qux') | |
.select("?uri ?uri1 ?foo ?bar ?baz ?qux"); | |
log(res); | |
} | |
/// memo ------------ | |
// | |
// QueryInterface | |
// URI | |
// GetSource | |
// GetSources | |
// GetTarget | |
// GetTargets | |
// Assert | |
// Unassert | |
// Change | |
// Move | |
// HasAssertion | |
// AddObserver | |
// RemoveObserver | |
// ArcLabelsIn | |
// ArcLabelsOut | |
// GetAllResources | |
// IsCommandEnabled | |
// DoCommand | |
// GetAllCmds | |
// hasArcIn | |
// hasArcOut | |
// beginUpdateBatch | |
// endUpdateBatch | |
// loaded | |
// Init | |
// Refresh | |
// Flush | |
// FlushTo | |
// readOnly | |
// beginLoad | |
// interrupt | |
// resume | |
// endLoad | |
// addXMLSinkObserver | |
// removeXMLSinkObserver | |
// onDataAvailable | |
// visitAllSubjects | |
// visitAllTriples | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment