diff options
Diffstat (limited to 'modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs')
| -rw-r--r-- | modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs | 434 |
1 files changed, 0 insertions, 434 deletions
diff --git a/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs b/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs deleted file mode 100644 index c6380c1..0000000 --- a/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs +++ /dev/null @@ -1,434 +0,0 @@ - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module creates a new API for accessing and modifying RDF graphs. The - * goal is to be able to serialise the graph in a human readable form. Also - * if the graph was originally loaded from an RDF/XML the serialisation should - * closely match the original with any new data closely following the existing - * layout. The output should always be compatible with Mozilla's RDF parser. - * - * This is all achieved by using a DOM Document to hold the current state of the - * graph in XML form. This can be initially loaded and parsed from disk or - * a blank document used for an empty graph. As assertions are added to the - * graph, appropriate DOM nodes are added to the document to represent them - * along with any necessary whitespace to properly layout the XML. - * - * In general the order of adding assertions to the graph will impact the form - * the serialisation takes. If a resource is first added as the object of an - * assertion then it will eventually be serialised inside the assertion's - * property element. If a resource is first added as the subject of an assertion - * then it will be serialised at the top level of the XML. - */ - -const NS_XML = "http://www.w3.org/XML/1998/namespace"; -const NS_XMLNS = "http://www.w3.org/2000/xmlns/"; -const NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; -const NS_NC = "http://home.netscape.com/NC-rdf#"; - -/* eslint prefer-template: 1 */ - -function isElement(obj) { - return Element.isInstance(obj); -} -function isText(obj) { - return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text"; -} - -/** - * Returns either an rdf namespaced attribute or an un-namespaced attribute - * value. Returns null if neither exists, - */ -function getRDFAttribute(element, name) { - if (element.hasAttributeNS(NS_RDF, name)) - return element.getAttributeNS(NS_RDF, name); - if (element.hasAttribute(name)) - return element.getAttribute(name); - return undefined; -} - -/** - * Represents an assertion in the datasource - */ -class RDFAssertion { - constructor(subject, predicate, object) { - // The subject on this assertion, an RDFSubject - this._subject = subject; - // The predicate, a string - this._predicate = predicate; - // The object, an RDFNode - this._object = object; - // The datasource this assertion exists in - this._ds = this._subject._ds; - // Marks that _DOMnode is the subject's element - this._isSubjectElement = false; - // The DOM node that represents this assertion. Could be a property element, - // a property attribute or the subject's element for rdf:type - this._DOMNode = null; - } - - getPredicate() { - return this._predicate; - } - - getObject() { - return this._object; - } -} - -class RDFNode { - equals(rdfnode) { - return (rdfnode.constructor === this.constructor && - rdfnode._value == this._value); - } -} - -/** - * A simple literal value - */ -export class RDFLiteral extends RDFNode { - constructor(value) { - super(); - this._value = value; - } - - getValue() { - return this._value; - } -} - -/** - * This is an RDF node that can be a subject so a resource or a blank node - */ -class RDFSubject extends RDFNode { - constructor(ds) { - super(); - // A lookup of the assertions with this as the subject. Keyed on predicate - this._assertions = {}; - // A lookup of the assertions with this as the object. Keyed on predicate - this._backwards = {}; - // The datasource this subject belongs to - this._ds = ds; - // The DOM elements in the document that represent this subject. Array of Element - this._elements = []; - } - - /** - * Parses the given Element from the DOM document - */ - /* eslint-disable complexity */ - _parseElement(element) { - this._elements.push(element); - - // There might be an inferred rdf:type assertion in the element name - if (element.namespaceURI != NS_RDF || - element.localName != "Description") { - var assertion = new RDFAssertion(this, RDF_R("type"), - this._ds.getResource(element.namespaceURI + element.localName)); - assertion._DOMnode = element; - assertion._isSubjectElement = true; - this._addAssertion(assertion); - } - - // Certain attributes can be literal properties - for (let attr of element.attributes) { - if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS || - attr.nodeName == "xmlns") - continue; - if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) && - (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName))) - continue; - var object = null; - if (attr.namespaceURI == NS_RDF) { - if (attr.localName == "type") - object = this._ds.getResource(attr.nodeValue); - } - if (!object) - object = new RDFLiteral(attr.nodeValue); - assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object); - assertion._DOMnode = attr; - this._addAssertion(assertion); - } - - var child = element.firstChild; - element.listCounter = 1; - while (child) { - if (isElement(child)) { - object = null; - var predicate = child.namespaceURI + child.localName; - if (child.namespaceURI == NS_RDF) { - if (child.localName == "li") { - predicate = RDF_R(`_${element.listCounter}`); - element.listCounter++; - } - } - - // Check for and bail out on unknown attributes on the property element - for (let attr of child.attributes) { - // Ignore XML namespaced attributes - if (attr.namespaceURI == NS_XML) - continue; - // These are reserved by XML for future use - if (attr.localName.substring(0, 3).toLowerCase() == "xml") - continue; - // We can handle these RDF attributes - if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) && - ["resource", "nodeID"].includes(attr.localName)) - continue; - // This is a special attribute we handle for compatibility with Mozilla RDF - if (attr.namespaceURI == NS_NC && - attr.localName == "parseType") - continue; - } - - var parseType = child.getAttributeNS(NS_NC, "parseType"); - - var resource = getRDFAttribute(child, "resource"); - var nodeID = getRDFAttribute(child, "nodeID"); - - if (resource !== undefined) { - var base = Services.io.newURI(element.baseURI); - object = this._ds.getResource(base.resolve(resource)); - } else if (nodeID !== undefined) { - object = this._ds.getBlankNode(nodeID); - } else { - var hasText = false; - var childElement = null; - var subchild = child.firstChild; - while (subchild) { - if (isText(subchild) && /\S/.test(subchild.nodeValue)) { - hasText = true; - } else if (isElement(subchild)) { - childElement = subchild; - } - subchild = subchild.nextSibling; - } - - if (childElement) { - object = this._ds._getSubjectForElement(childElement); - object._parseElement(childElement); - } else - object = new RDFLiteral(child.textContent); - } - - assertion = new RDFAssertion(this, predicate, object); - this._addAssertion(assertion); - assertion._DOMnode = child; - } - child = child.nextSibling; - } - } - /* eslint-enable complexity */ - - /** - * Adds a new assertion to the internal hashes. Should be called for every - * new assertion parsed or created programmatically. - */ - _addAssertion(assertion) { - var predicate = assertion.getPredicate(); - if (predicate in this._assertions) - this._assertions[predicate].push(assertion); - else - this._assertions[predicate] = [ assertion ]; - - var object = assertion.getObject(); - if (object instanceof RDFSubject) { - // Create reverse assertion - if (predicate in object._backwards) - object._backwards[predicate].push(assertion); - else - object._backwards[predicate] = [ assertion ]; - } - } - - /** - * Returns all objects in assertions with this subject and the given predicate. - */ - getObjects(predicate) { - if (predicate in this._assertions) - return Array.from(this._assertions[predicate], - i => i.getObject()); - - return []; - } - - /** - * Retrieves the first property value for the given predicate. - */ - getProperty(predicate) { - if (predicate in this._assertions) - return this._assertions[predicate][0].getObject(); - return null; - } -} - -/** - * Creates a new RDFResource for the datasource. Private. - */ -export class RDFResource extends RDFSubject { - constructor(ds, uri) { - super(ds); - // This is the uri that the resource represents. - this._uri = uri; - } -} - -/** - * Creates a new blank node. Private. - */ -export class RDFBlankNode extends RDFSubject { - constructor(ds, nodeID) { - super(ds); - // The nodeID of this node. May be null if there is no ID. - this._nodeID = nodeID; - } - - /** - * Sets attributes on the DOM element to mark it as representing this node - */ - _applyToElement(element) { - if (!this._nodeID) - return; - if (USE_RDFNS_ATTR) { - var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID")); - element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID); - } else { - element.setAttribute("nodeID", this._nodeID); - } - } - - /** - * Creates a new Element in the document for holding assertions about this - * subject. The URI controls what tagname to use. - */ - _createNewElement(uri) { - // If there are already nodes representing this in the document then we need - // a nodeID to match them - if (!this._nodeID && this._elements.length > 0) { - this._ds._createNodeID(this); - for (let element of this._elements) - this._applyToElement(element); - } - - return super._createNewElement.call(uri); - } - - /** - * Adds a reference to this node to the given property Element. - */ - _addReferenceToElement(element) { - if (this._elements.length > 0 && !this._nodeID) { - // In document elsewhere already - // Create a node ID and update the other nodes referencing - this._ds._createNodeID(this); - for (let element of this._elements) - this._applyToElement(element); - } - - if (this._nodeID) { - if (USE_RDFNS_ATTR) { - let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID")); - element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID); - } else { - element.setAttribute("nodeID", this._nodeID); - } - } else { - // Add the empty blank node, this is generally right since further - // assertions will be added to fill this out - var newelement = this._ds._addElement(element, RDF_R("Description")); - newelement.listCounter = 1; - this._elements.push(newelement); - } - } - - /** - * Removes any reference to this node from the given property Element. - */ - _removeReferenceFromElement(element) { - if (element.hasAttributeNS(NS_RDF, "nodeID")) - element.removeAttributeNS(NS_RDF, "nodeID"); - if (element.hasAttribute("nodeID")) - element.removeAttribute("nodeID"); - } - - getNodeID() { - return this._nodeID; - } -} - -/** - * Creates a new RDFDataSource from the given document. The document will be - * changed as assertions are added and removed to the RDF. Pass a null document - * to start with an empty graph. - */ -export class RDFDataSource { - constructor(document) { - // All known resources, indexed on URI - this._resources = {}; - // All blank nodes - this._allBlankNodes = []; - - // The underlying DOM document for this datasource - this._document = document; - this._parseDocument(); - } - - static loadFromString(text) { - let parser = new DOMParser(); - let document = parser.parseFromString(text, "application/xml"); - - return new this(document); - } - - /** - * Returns an rdf subject for the given DOM Element. If the subject has not - * been seen before a new one is created. - */ - _getSubjectForElement(element) { - var about = getRDFAttribute(element, "about"); - - if (about !== undefined) { - let base = Services.io.newURI(element.baseURI); - return this.getResource(base.resolve(about)); - } - return this.getBlankNode(null); - } - - /** - * Parses the document for subjects at the top level. - */ - _parseDocument() { - var domnode = this._document.documentElement.firstChild; - while (domnode) { - if (isElement(domnode)) { - var subject = this._getSubjectForElement(domnode); - subject._parseElement(domnode); - } - domnode = domnode.nextSibling; - } - } - - /** - * Gets a blank node. nodeID may be null and if so a new blank node is created. - * If a nodeID is given then the blank node with that ID is returned or created. - */ - getBlankNode(nodeID) { - var rdfnode = new RDFBlankNode(this, nodeID); - this._allBlankNodes.push(rdfnode); - return rdfnode; - } - - /** - * Gets the resource for the URI. The resource is created if it has not been - * used already. - */ - getResource(uri) { - if (uri in this._resources) - return this._resources[uri]; - - var resource = new RDFResource(this, uri); - this._resources[uri] = resource; - return resource; - } -}
\ No newline at end of file |
