1var wikibase = wikibase || {}; 2wikibase.queryService = wikibase.queryService || {}; 3wikibase.queryService.ui = wikibase.queryService.ui || {}; 4wikibase.queryService.ui.queryHelper = wikibase.queryService.ui.queryHelper || {}; 5 6wikibase.queryService.ui.queryHelper.SparqlQuery = ( function( $, wikibase, sparqljs ) { 7 'use strict'; 8 9 /** 10 * A SPARQL query representation 11 * 12 * @class wikibase.queryService.ui.queryHelper.SparqlQuery 13 * @license GNU GPL v2+ 14 * 15 * @author Jonas Kress 16 * @constructor 17 * @param {Object} query 18 */ 19 function SELF( query ) { 20 this._query = query; 21 } 22 23 /** 24 * @property {Object} 25 * @private 26 */ 27 SELF.prototype._query = null; 28 29 /** 30 * @property {Array} 31 * @private 32 */ 33 SELF.prototype._queryComments = null; 34 35 /** 36 * Set the SPARQL query string 37 * 38 * @param {string} query SPARQL query string 39 * @param {Object} prefixes 40 */ 41 SELF.prototype.parse = function( query, prefixes ) { 42 var parser = new sparqljs.Parser( prefixes ), 43 queryComments = []; 44 this._query = parser.parse( query ); 45 $.each( query.split( '\n' ), function( index, line ) { 46 if ( line.indexOf( '#' ) === 0 ) { 47 queryComments.push( line ); 48 } 49 } ); 50 this._queryComments = queryComments; 51 }; 52 53 /** 54 * Get the SPARQL query string 55 * 56 * @return {string|null} 57 */ 58 SELF.prototype.getQueryString = function() { 59 try { 60 var sparql = new sparqljs.Generator().stringify( this._query ), 61 comments = this._queryComments.join( '\n' ).trim(); 62 63 if ( comments !== '' ) { 64 return comments + '\n' + sparql; 65 } else { 66 return sparql; 67 } 68 } catch ( e ) { 69 return null; 70 } 71 }; 72 73 /** 74 * @return {number|null} 75 */ 76 SELF.prototype.getLimit = function() { 77 return this._query.limit || null; 78 }; 79 80 /** 81 * @param {number|null} limit 82 * @return {wikibase.queryService.ui.queryHelper.SparqlQuery} 83 */ 84 SELF.prototype.setLimit = function( limit ) { 85 if ( !limit ) { 86 delete this._query.limit; 87 } else { 88 this._query.limit = limit; 89 } 90 91 return this; 92 }; 93 94 /** 95 * Check whether a variable is used in the SELECT clause of query 96 * 97 * @param {string} name of variable e.g. ?foo 98 */ 99 SELF.prototype.hasVariable = function( name ) { 100 if ( this._query.variables.length === 0 ) { 101 return false; 102 } 103 104 return this._query.variables.indexOf( name ) >= 0; 105 }; 106 107 /** 108 * Check whether query uses wildcard SELECT * 109 */ 110 SELF.prototype.isWildcardQuery = function() { 111 if ( this._query.variables && this._query.variables[0] === '*' ) { 112 return true; 113 } 114 115 return false; 116 }; 117 118 /** 119 * Convert wildcard query to use variables 120 * 121 * @deprecated workaround for T171194 122 */ 123 SELF.prototype.convertWildcardQueryToUseVariables = function() { 124 125 if ( !this.isWildcardQuery() ) { 126 return; 127 } 128 129 var variables = this.getQueryString().match( /(\?\w+)/g ); 130 this._query.variables = $.unique( variables ) || '*'; 131 }; 132 133 /** 134 * Add a variable to the query SELECT 135 * 136 * @param {string} name 137 * @return {wikibase.queryService.ui.queryHelper.SparqlQuery} 138 */ 139 SELF.prototype.addVariable = function( name ) { 140 if ( !name || !name.startsWith( '?' ) ) { 141 return this; 142 } 143 144 if ( this._query.variables.indexOf( name ) >= 0 ) { 145 return this; 146 } 147 148 if ( this.isWildcardQuery() ) { 149 return this; 150 } 151 152 this._query.variables.push( name ); 153 154 return this; 155 }; 156 157 /** 158 * Remove a variable from the query SELECT 159 * 160 * @param {string} name 161 */ 162 SELF.prototype.removeVariable = function( name ) { 163 if ( !name.startsWith( '?' ) ) { 164 return; 165 } 166 167 if ( this.isWildcardQuery() ) { 168 return; 169 } 170 171 var index = this._query.variables.indexOf( name ); 172 if ( index >= 0 ) { 173 this._query.variables.splice( index, 1 ); 174 } 175 176 if ( this._query.variables.length === 0 ) { 177 this._query.variables.push( '*' ); 178 } 179 }; 180 181 /** 182 * Removes unused variables from the query SELECT 183 * Disclaimer: may remove too much 184 * 185 * @param {string[]} [variables] cleanup only variables in this list 186 */ 187 SELF.prototype.cleanupVariables = function( variables ) { 188 var self = this, 189 usedVariables = this.getTripleVariables(); 190 191 // TODO check sub queries 192 var toRemove = this._query.variables.filter( function ( variable ) { 193 if ( variables && variables.indexOf( variable ) === -1 ) { 194 return false;
195 } 196 197 if ( usedVariables.indexOf( variable ) === -1 ) { 198 return true; 199 } 200 201 return false; 202 } ); 203 204 toRemove.map( function ( v ) { 205 self.removeVariable( v ); 206 } ); 207 }; 208 209 /** 210 * Get triples defined in this query 211 * 212 * @return {Object} 213 */ 214 SELF.prototype.getTriples = function( node, isOptional ) { 215 var triples = []; 216 if ( !node ) { 217 node = this._query.where; 218 } 219 if ( !isOptional ) { 220 isOptional = false; 221 } 222 223 var self = this; 224 $.each( node, function( k, v ) { 225 if ( v.type && v.type === 'bgp' ) { 226 triples = triples.concat( self._createTriples( v.triples, isOptional ) ); 227 } 228 if ( v.type && v.type === 'optional' ) { 229 triples = triples.concat( self.getTriples( v.patterns, true ) ); 230 } 231 if ( v.type && v.type === 'union' ) { 232 triples = triples.concat( self.getTriples( v.patterns, false ) ); 233 } 234 } ); 235 236 return triples; 237 }; 238 239 /** 240 * Get bindings defined in this query 241 * 242 * @return {Object} 243 */ 244 SELF.prototype.getBindings = function() { 245 var bindings = {}; 246 247 $.each( this._query.where, function( k, v ) { 248 if ( v.type && v.type === 'bind' ) { 249 bindings[ v.variable ] = v; 250 } 251 } ); 252 253 return bindings; 254 }; 255 256 /** 257 * @private 258 */ 259 SELF.prototype._createTriples = function( triplesData, isOptional ) { 260 var self = this, 261 triples = []; 262 263 $.each( triplesData, function( i, triple ) { 264 triples.push( { 265 optional: isOptional, 266 query: self, 267 triple: triple, 268 remove: function() { 269 triplesData.splice( i, 1 ); 270 } 271 } ); 272 } ); 273 274 return triples; 275 }; 276 277 /** 278 * Get triples defined in this query 279 * 280 * @return {wikibase.queryService.ui.queryHelper.SparqlQuery[]} 281 */ 282 SELF.prototype.getSubQueries = function() { 283 var queries = []; 284 285 $.each( this._query.where, function( k, v ) { 286 if ( v.queryType ) { 287 queries.push( new SELF( v ) ); 288 } 289 } ); 290 291 return queries; 292 }; 293 294 /** 295 * Add a triple to the query 296 * 297 * @param {string} subject 298 * @param {string} predicate 299 * @param {string} object 300 * @param {boolean} isOptional 301 */ 302 SELF.prototype.addTriple = function( subject, predicate, object, isOptional ) { 303 var triple = { 304 type: 'bgp', 305 triples: [ 306 { 307 subject: subject, 308 predicate: predicate, 309 object: object 310 } 311 ] 312 }; 313 314 if ( isOptional ) { 315 var optionalTriple = { 316 type: 'optional', 317 patterns: [ 318 triple 319 ] 320 }; 321 this._query.where.push( optionalTriple ); 322 } else { 323 this._query.where.push( triple ); 324 } 325 326 return this._createTriples( triple.triples, isOptional )[0]; 327 }; 328 329 /** 330 * Get all variables used in triples 331 * 332 * @return {string[]} 333 */ 334 SELF.prototype.getTripleVariables = function() { 335 var variables = {}; 336 337 $.each( this.getTriples(), function( i, t ) { 338 if ( typeof t.triple.subject === 'string' && t.triple.subject.startsWith( '?' ) ) { 339 variables[t.triple.subject] = true; 340 } 341 if ( typeof t.triple.predicate === 'string' && t.triple.predicate.startsWith( '?' ) ) { 342 variables[t.triple.predicate] = true; 343 } 344 if ( typeof t.triple.object === 'string' && t.triple.object.startsWith( '?' ) ) { 345 variables[t.triple.object] = true; 346 } 347 348 } ); 349 350 return Object.keys( variables ); 351 }; 352 353 /** 354 * Get variables that are bound to a certain value 355 * 356 * @return {string[]} 357 */ 358 SELF.prototype.getBoundVariables = function() { 359 var variables = {}; 360 361 $.each( this.getTriples(), function( i, t ) { 362 if ( t.triple.subject.startsWith( '?' ) && !t.triple.object.startsWith( '?' ) ) { 363 variables[t.triple.subject] = true; 364 } 365 366 if ( !t.triple.subject.startsWith( '?' ) && t.triple.object.startsWith( '?' ) ) { 367 variables[t.triple.object] = true; 368 } 369 } ); 370 371 return Object.keys( variables ); 372 }; 373 374 /** 375 * Get services defined in query 376 * 377 * @return {object[]} 378 */ 379 SELF.prototype.getServices = function() { 380 var services = []; 381 382 $.each( this._query.where, function( i, node ) { 383 if ( node && node.type === 'service' ) { 384 services.push( node ); 385 } 386 387 } ); 388 389 return services; 390 }; 391 392 /** 393 * Remove a certain service from the query 394 * 395 * @param {string} serviceId of the service to be removed 396 * @return {wikibase.queryService.ui.queryHelper.SparqlQuery} 397 */ 398 SELF.prototype.removeService = function( serviceId ) { 399 var self = this; 400 401 $.each( this._query.where, function ( i, node ) { 402 if ( node.type === 'service' && node.name === serviceId ) { 403 delete self._query.where[i]; 404 } 405 406 } ); 407 408 return this; 409 }; 410 411 /** 412 * Get the content of a content beginning with start. 413 * 414 * For example, on a query with '#foo=bar', 415 * getCommentContent( 'foo=' ) will return 'bar'. 416 * 417 * @param {string} start The beginning of the comment, *without* the comment mark ('#'). 418 * 419 * @return {?string} 420 */ 421 SELF.prototype.getCommentContent = function( start ) { 422 var i, comment; 423 for ( i = 0; i < this._queryComments.length; i++ ) { 424 comment = this._queryComments[ i ]; 425 if ( comment.startsWith( '#' + start ) ) { 426 return comment.substring( 1 + start.length ); 427 } 428 } 429 return null; 430 }; 431 432 /** 433 * Clone query 434 * 435 * @return {wikibase.queryService.ui.queryHelper.SparqlQuery} 436 */ 437 SELF.prototype.clone = function() { 438 var query = new SELF(); 439 query.parse( this.getQueryString() ); 440 return query; 441 }; 442 443 return SELF; 444}( jQuery, wikibase, sparqljs ) );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.