1 /** 2 * Toby Inkster's Utility Functions for SPARQL 3 * 4 * Copyright (c) 2011 Toby Inkster <http://tobyinkster.co.uk/>. 5 * Portions copyright Alexandru Marasteanu <http://alexei.417.ro/>, 6 * and Robert Kieffer. 7 * 8 * This document defines a set of functions suitable for using 9 * within SPARQL implementations. It provides reference implementations 10 * in ECMAScript, though SPARQL engines supporting this set of 11 * functions are expected to provide native implementations for them. 12 * 13 * Each function defined in this document can be identified with its 14 * URI <http://buzzword.org.uk/2011/functions/util#xxxx> where "xxxx" 15 * is the function name. 16 * 17 * The initial argument passed to each function is assumed to be an 18 * object providing the RDFEnvironment interface as defined by the 19 * draft W3C RDF API. 20 * 21 * http://www.w3.org/2010/02/rdfa/sources/rdf-api/#idl-def-RDFEnvironment 22 * 23 * Further arguments are taken to provide the RDFNode interface. 24 * 25 * http://www.w3.org/2010/02/rdfa/sources/rdf-api/#idl-def-RDFNode 26 * 27 * This program is free software; you can redistribute it and/or modify it under 28 * the terms of the GNU General Public License as published by the Free Software 29 * Foundation; either version 2 of the License, or (at your option) any later 30 * version. 31 * 32 * This program is distributed in the hope that it will be useful, but WITHOUT 33 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 34 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 35 * details. 36 * 37 * You should have received a copy of the GNU General Public License along with 38 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 39 * Place, Suite 330, Boston, MA 02111-1307 USA 40 */ 41 42 /** 43 * Convert a literal to majuscule 44 * @param {RDFEnvironment} $environment The environment 45 * @param {Literal} $node The literal to be uppercased 46 * @returns An uppercase literal 47 * @type Literal 48 */ 49 function uc ($environment, $node) 50 { 51 if ($node.interfaceName != "Literal") 52 throw new Error("Argument be a literal."); 53 54 return $environment.createLiteral( 55 $node.value.toUpperCase(), 56 $node.language, 57 $node.datatype 58 ); 59 } 60 61 /** 62 * Convert a literal to minuscule 63 * @param {RDFEnvironment} $environment The environment 64 * @param {Literal} $node The literal to be lowercased 65 * @returns A lowercase literal 66 * @type Literal 67 */ 68 function lc ($environment, $node) 69 { 70 if ($node.interfaceName != "Literal") 71 throw new Error("Argument be a literal."); 72 73 return $environment.createLiteral( 74 $node.value.toLowerCase(), 75 $node.language, 76 $node.datatype 77 ); 78 } 79 80 /** 81 * Strip leading whitespace 82 * @param {RDFEnvironment} $environment The environment 83 * @param {Literal} $node The literal to be trimmed 84 * @returns A literal 85 * @type Literal 86 */ 87 function ltrim ($environment, $node) 88 { 89 if ($node.interfaceName != "Literal") 90 throw new Error("Argument be a literal."); 91 92 return $environment.createLiteral( 93 $node.value.replace(/^\s+/,""), 94 $node.language, 95 $node.datatype 96 ); 97 } 98 99 /** 100 * Strip trailing whitespace 101 * @param {RDFEnvironment} $environment The environment 102 * @param {Literal} $node The literal to be trimmed 103 * @returns A literal 104 * @type Literal 105 */ 106 function rtrim ($environment, $node) 107 { 108 if ($node.interfaceName != "Literal") 109 throw new Error("Argument be a literal."); 110 111 return $environment.createLiteral( 112 $node.value.replace(/\s+$/,""), 113 $node.language, 114 $node.datatype 115 ); 116 } 117 118 /** 119 * Strip leading and trailing whitespace 120 * @param {RDFEnvironment} $environment The environment 121 * @param {Literal} $node The literal to be trimmed 122 * @returns A literal 123 * @type Literal 124 */ 125 function trim ($environment, $node) 126 { 127 if ($node.interfaceName != "Literal") 128 throw new Error("Argument be a literal."); 129 130 return $environment.createLiteral( 131 $node.value.replace(/^\s+|\s+$/g,""), 132 $node.language, 133 $node.datatype 134 ); 135 } 136 137 /** 138 * Interpolate values into a string pattern 139 * @param {RDFEnvironment} $environment The environment 140 * @param {Literal} $pattern The pattern into which values should be interpolated 141 * @returns A literal 142 * @type Literal 143 */ 144 function sprintf ($environment, $pattern) 145 { 146 if ($pattern.interfaceName != "Literal") 147 throw new Error("Pattern be a literal."); 148 149 var str_repeat = function (i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); } 150 var _sprintf = function (f, params) 151 { 152 var i = 0, a, o = [], m, p, c, x; 153 while (f) 154 { 155 if (m = /^[^\x25]+/.exec(f)) 156 o.push(m[0]); 157 else if (m = /^\x25{2}/.exec(f)) 158 o.push('%'); 159 else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) 160 { 161 if (((a = params[m[1] || i++]) == null) || (a == undefined)) 162 throw new Error("Too few arguments."); 163 if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) 164 throw new Error("Expecting number but found " + typeof(a)); 165 switch (m[7]) 166 { 167 case 'b': a = a.toString(2); break; 168 case 'c': a = String.fromCharCode(a); break; 169 case 'd': a = parseInt(a); break; 170 case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; 171 case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; 172 case 'o': a = a.toString(8); break; 173 case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; 174 case 'u': a = Math.abs(a); break; 175 case 'x': a = a.toString(16); break; 176 case 'X': a = a.toString(16).toUpperCase(); break; 177 } 178 a = (/[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a); 179 c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' '; 180 x = m[5] - String(a).length; 181 p = m[5] ? str_repeat(c, x) : ''; 182 o.push(m[4] ? a + p : p + a); 183 } 184 else throw ("Huh ?!"); 185 f = f.substring(m[0].length); 186 } 187 return o.join(''); 188 } 189 190 var argList = new Array(); 191 for (var i=2; arguments[i] != undefined; i++) 192 { 193 argList.push(arguments[i].value); 194 } 195 196 return $environment.createLiteral( 197 _sprintf($pattern.value, argList), 198 $pattern.language, 199 $pattern.datatype 200 ); 201 } 202 203 /** 204 * Generate a UUID 205 * @param {RDFEnvironment} $environment The environment 206 * @returns A literal 207 * @type Literal 208 */ 209 function uuid ($environment) 210 { 211 // via http://www.broofa.com/Tools/Math.uuid.js 212 var _uuidCompact = function() { 213 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 214 var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 215 return v.toString(16); 216 }).toUpperCase(); 217 }; 218 219 return $environment.createLiteral(_uuidCompact(), null, null); 220 } 221 222 /** 223 * Generate a UUID, and format as a URI 224 * @param {RDFEnvironment} $environment The environment 225 * @returns A named node (URI) 226 * @type NamedNode 227 */ 228 function uuid_uri ($environment) 229 { 230 return $environment.createNamedNode("urn:uuid:" + uuid($environment).value); 231 } 232 233 /** 234 * Generate an OID 235 * @param {RDFEnvironment} $environment The environment 236 * @returns A literal 237 * @type Literal 238 */ 239 function oid ($environment) 240 { 241 var $oid = "1.3.6.1.4.1.33926.9"; 242 var $uuid = uuid($environment).value.replace(/\-/g, ""); 243 244 while ($uuid.length) 245 { 246 var $chunk = $uuid.substr(0, 4); 247 $uuid = $uuid.substr(4); 248 $oid += "." + parseInt($chunk, 16); 249 } 250 251 return $environment.createLiteral($oid, null, null); 252 } 253 254 /** 255 * Generate an OID, and format as a URI 256 * @param {RDFEnvironment} $environment The environment 257 * @returns A named node (URI) 258 * @type NamedNode 259 */ 260 function oid_uri ($environment) 261 { 262 return $environment.createNamedNode("urn:oid:" + oid($environment).value); 263 } 264 265 /** 266 * Generate a random number 267 * @param {RDFEnvironment} $environment The environment 268 * @param {Literal} $upper The upper bound 269 * @param {Literal} $lower The lower bound (optional, defaults to 0) 270 * @returns A literal with same datatype as $upper. 271 * @type Literal 272 */ 273 function rand ($environment, $upper, $lower) 274 { 275 if ($lower == undefined) 276 $lower = $environment.createLiteral(0, null, $upper.datatype); 277 278 if ($lower.interfaceName != "Literal") 279 throw new Error("Lower bound be a literal."); 280 281 if ($upper.interfaceName != "Literal") 282 throw new Error("Upper bound be a literal."); 283 284 if ($lower.datatype.value != $upper.datatype.value) 285 throw new Error("Lower and upper bound must have same datatype."); 286 287 if ($upper.interfaceName != "Literal" 288 || !$upper.datatype.value.match(/^http:\/\/www.w3.org\/2001\/XMLSchema#(decimal|float|double|integer|non(Positive|Negative)Integer|(positive|negative)Integer|long|int|short|byte|unsigned(Long|Int|Short|Byte))$/)) 289 throw new Error("Upper bound must have numeric datatype."); 290 291 var $rand = $lower.value + (Math.random() * ($upper.value - $lower.value)); 292 293 if ($upper.datatype.value.match(/^http:\/\/www.w3.org\/2001\/XMLSchema#(integer|non(Positive|Negative)Integer|(positive|negative)Integer|long|int|short|byte|unsigned(Long|Int|Short|Byte))$/)) 294 $rand = Math.floor($rand); 295 296 return $environment.createLiteral($rand, null, $upper.datatype); 297 } 298 299 /** 300 * Split an IRI on "#" and return the first half 301 * @param {RDFEnvironment} $environment The environment 302 * @param {NamedNode} $node The URI to split 303 * @returns The first part of the URI 304 * @type NamedNode 305 */ 306 function defragment ($environment, $node) 307 { 308 if ($node.interfaceName != "NamedNode") 309 throw new Error("Argument be a named node."); 310 311 var $parts = $node.value.split("#"); 312 313 return $environment.createNamedNode($parts[0]); 314 } 315 316 /** 317 * Replace all occurances of a given string within another. 318 * @param {RDFEnvironment} $environment The environment 319 * @param {Literal} $needle The string to search for 320 * @param {Literal} $replacement The string with which to replace matches 321 * @param {Literal} $haystack The literal to search within 322 * @returns The $haystack literal with all instances of $needle replaced 323 * @type Literal 324 */ 325 function str_replace ($environment, $needle, $replacement, $haystack) 326 { 327 throw new Error("Not implemented yet."); 328 } 329 330 /** 331 * Replace all matches for a given expression within a string. 332 * @param {RDFEnvironment} $environment The environment 333 * @param {Literal} $needle The regular expression to search for 334 * @param {Literal} $replacement The string with which to replace matches 335 * @param {Literal} $haystack The literal to search within 336 * @param {Literal} $modes Defaults to "g" 337 * @returns The $haystack literal with all substrings matching $needle replaced 338 * @type Literal 339 */ 340 function preg_replace ($environment, $needle, $replacement, $haystack, $modes) 341 { 342 throw new Error("Not implemented yet."); 343 } 344 345 /** 346 * Names blank nodes. 347 * 348 * Given a node, if it's named or a literal, returns as is. If blank, 349 * returns a URI for the node. Idempotent for any particular RDFEnvironment. 350 * That is, within an execution, given the same blank node multiple 351 * times will always return the same URI. 352 * 353 * @param {RDFEnvironment} $environment The environment 354 * @param {RDFNode} $node The node to name 355 * @param {Literal} $scheme "OID" or "UUID" (default). Optional. 356 * @returns A named node or literal 357 * @type RDFNode 358 */ 359 function skolem ($environment, $node, $scheme) 360 { 361 throw new Error("Not implemented yet."); 362 } 363 364 /** 365 * Searches an XMLLiteral for an XPath. 366 * @param {RDFEnvironment} $environment The environment 367 * @param {Literal} $xpath The path to search for 368 * @param {Literal} $xmlliteral The literal to search within 369 * @param {Literal} $index If multiple matches found, which to return. Defaults to 0. 370 * @returns An XMLLiteral or plain literal 371 * @type Literal 372 */ 373 function find_xpath ($environment, $xpath, $xmlliteral, $index) 374 { 375 throw new Error("Not implemented yet."); 376 } 377