UNPKG

link-rdflib

Version:

an RDF library for node.js, patched for speed.

224 lines (206 loc) 6.39 kB
'use strict' const ClassOrder = require('./class-order') const NamedNode = require('./named-node') const Term = require('./term') const XSD = require('./xsd') class Literal extends Term { constructor (value, language, datatype) { super() this.termType = Literal.termType this.value = value if (language) { this.lang = language datatype = XSD.langString } // If not specified, a literal has the implied XSD.string default datatype this.datatype = datatype ? NamedNode.find(datatype) : XSD.string const existing = Literal.findLiteralByValue(value, language, datatype) if (existing) { return existing } Literal.mem(this) } copy () { return Literal.find(this.value, this.lang, this.datatype) } [Symbol.toPrimitive](_) { if ([undefined, XSD.string, XSD.langString].includes(this.datatype)) { return this.value } if (this.datatype === XSD.boolean) { return this.value === 'true' || this.value === '1' } if ([XSD.date, XSD.dateTime, XSD.dateTimeStamp].includes(this.datatype)) { return new Date(this.value) } if ([XSD.decimal, XSD.double, XSD.integer, XSD.int, XSD.float, XSD.long].includes(this.datatype)) { return Number(this.value) } return undefined } get language () { return this.lang } set language (language) { this.lang = language || '' } toNT () { if (typeof this.value === 'number') { return this.toString() } else if (typeof this.value !== 'string') { throw new Error('Value of RDF literal is not string or number: ' + this.value) } var str = this.value str = str.replace(/\\/g, '\\\\') str = str.replace(/\"/g, '\\"') str = str.replace(/\n/g, '\\n') str = '"' + str + '"' if (this.language) { str += '@' + this.language } else if (this.datatype !== XSD.string) { // Only add datatype if it's not a string str += '^^' + this.datatype.toCanonical() } return str } /** * Assigns an index number and adds a Literal instance to the indices * @private * @param lit The Literal instance to register * @return {Literal} The updated Literal instance */ static mem(lit) { if (lit.sI) { throw new Error(`Literal ${lit} already registered`) } lit.sI = ++Term.termIndex const dtIndex = (lit.datatype || require('./xsd').string).sI if (!Term.litMap[dtIndex]) { Term.litMap[dtIndex] = [] } if (lit.language) { if (!Term.litMap[dtIndex][lit.value]) { Term.litMap[dtIndex][lit.value] = [] } Term.termMap[lit.sI] = Term.litMap[dtIndex][lit.value][lit.language] = lit } else { Term.termMap[lit.sI] = Term.litMap[dtIndex][lit.value] = lit } return lit } /** @private */ static findLiteralByValue(strValue, lang = undefined, datatype) { let fromMap // Language strings need an additional index layer if (lang) { const langStringIndex = require('./xsd').langString.sI if (!Term.litMap[langStringIndex]) { Term.litMap[langStringIndex] = [] } if (!Term.litMap[langStringIndex][strValue]) { Term.litMap[langStringIndex][strValue] = [] } fromMap = Term.litMap[langStringIndex][strValue][lang] } else { const dtIndex = (datatype || require('./xsd').string).sI fromMap = Term.litMap[dtIndex] && Term.litMap[dtIndex][strValue] } return fromMap } /** * Retrieve or create a Literal by its datatype, value, and language * @param value {Object} The value of the literal * @param lang? {string} The language of the literal (will force datatype xsd:langString) * @param datatype? {NamedNode} The IRI of the datatype * @return {Literal} The resolved or created Literal */ static find(value, lang = undefined, datatype) { const strValue = value.toString() const existing = Literal.findLiteralByValue(strValue, lang, datatype) if (existing) { return existing } return new Literal(strValue, lang, datatype) } /** * @method fromBoolean * @static * @param value {Boolean} * @return {Literal} */ static fromBoolean (value) { let strValue = value ? '1' : '0' return Literal.find(strValue, null, XSD.boolean) } /** * @method fromDate * @static * @param value {Date} * @return {Literal} */ static fromDate (value) { if (!(value instanceof Date)) { throw new TypeError('Invalid argument to Literal.fromDate()') } let d2 = function (x) { return ('' + (100 + x)).slice(1, 3) } let date = '' + value.getUTCFullYear() + '-' + d2(value.getUTCMonth() + 1) + '-' + d2(value.getUTCDate()) + 'T' + d2(value.getUTCHours()) + ':' + d2(value.getUTCMinutes()) + ':' + d2(value.getUTCSeconds()) + 'Z' return Literal.find(date, null, XSD.dateTime) } /** * @method fromNumber * @static * @param value {Number} * @return {Literal} */ static fromNumber (value) { if (typeof value !== 'number') { throw new TypeError('Invalid argument to Literal.fromNumber()') } let datatype const strValue = value.toString() if (strValue.indexOf('e') < 0 && Math.abs(value) <= Number.MAX_SAFE_INTEGER) { datatype = Number.isInteger(value) ? XSD.integer : XSD.decimal } else { datatype = XSD.double } return Literal.find(strValue, null, datatype) } /** * @method fromValue * @param value * @return {Literal} */ static fromValue (value) { if (typeof value === 'undefined' || value === null) { return value } if (typeof value === 'object' && value.termType) { // this is a Node instance return value } switch (typeof value) { case 'object': if (value instanceof Date) { return Literal.fromDate(value) } case 'boolean': return Literal.fromBoolean(value) case 'number': return Literal.fromNumber(value) case 'string': return Literal.find(value, null, XSD.string) } throw new Error("Can't make literal from " + value + ' of type ' + typeof value) } } Literal.termType = 'Literal' Literal.prototype.classOrder = ClassOrder['Literal'] Literal.prototype.datatype = XSD.string Literal.prototype.lang = '' Literal.prototype.isVar = 0 module.exports = Literal