Mercurial > repos > guerler > springsuite
diff planemo/lib/python3.7/site-packages/rdflib/plugins/sparql/operators.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/planemo/lib/python3.7/site-packages/rdflib/plugins/sparql/operators.py Fri Jul 31 00:32:28 2020 -0400 @@ -0,0 +1,1010 @@ +""" +This contains evaluation functions for expressions + +They get bound as instances-methods to the CompValue objects from parserutils +using setEvalFn + +""" + +import sys +import re +import math +import random +import uuid +import hashlib +import urllib.request, urllib.error, urllib.parse + +from decimal import Decimal, ROUND_HALF_UP, InvalidOperation + +import operator as pyop # python operators + +import isodate + +from rdflib.plugins.sparql.parserutils import CompValue, Expr +from rdflib.plugins.sparql.datatypes import XSD_DTs, type_promotion +from rdflib import URIRef, BNode, Variable, Literal, XSD, RDF +from rdflib.term import Node + +from pyparsing import ParseResults + +from rdflib.plugins.sparql.sparql import SPARQLError, SPARQLTypeError +from functools import reduce + + +# closed namespace, langString isn't in it +RDF_langString = URIRef(RDF.uri + "langString") + + +def Builtin_IRI(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-iri + """ + + a = expr.arg + + if isinstance(a, URIRef): + return a + if isinstance(a, Literal): + return ctx.prologue.absolutize(URIRef(a)) + + raise SPARQLError('IRI function only accepts URIRefs or Literals/Strings!') + + +def Builtin_isBLANK(expr, ctx): + return Literal(isinstance(expr.arg, BNode)) + + +def Builtin_isLITERAL(expr, ctx): + return Literal(isinstance(expr.arg, Literal)) + + +def Builtin_isIRI(expr, ctx): + return Literal(isinstance(expr.arg, URIRef)) + + +def Builtin_isNUMERIC(expr, ctx): + try: + numeric(expr.arg) + return Literal(True) + except: + return Literal(False) + + +def Builtin_BNODE(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-bnode + """ + + a = expr.arg + + if a is None: + return BNode() + + if isinstance(a, Literal): + return ctx.bnodes[a] # defaultdict does the right thing + + raise SPARQLError( + 'BNode function only accepts no argument or literal/string') + + +def Builtin_ABS(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-abs + """ + + return Literal(abs(numeric(expr.arg))) + + +def Builtin_IF(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-if + """ + + return expr.arg2 if EBV(expr.arg1) else expr.arg3 + + +def Builtin_RAND(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#idp2133952 + """ + + return Literal(random.random()) + + +def Builtin_UUID(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strdt + """ + + return URIRef(uuid.uuid4().urn) + + +def Builtin_STRUUID(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strdt + """ + + return Literal(str(uuid.uuid4())) + + +def Builtin_MD5(expr, ctx): + s = string(expr.arg).encode("utf-8") + return Literal(hashlib.md5(s).hexdigest()) + + +def Builtin_SHA1(expr, ctx): + s = string(expr.arg).encode("utf-8") + return Literal(hashlib.sha1(s).hexdigest()) + + +def Builtin_SHA256(expr, ctx): + s = string(expr.arg).encode("utf-8") + return Literal(hashlib.sha256(s).hexdigest()) + + +def Builtin_SHA384(expr, ctx): + s = string(expr.arg).encode("utf-8") + return Literal(hashlib.sha384(s).hexdigest()) + + +def Builtin_SHA512(expr, ctx): + s = string(expr.arg).encode("utf-8") + return Literal(hashlib.sha512(s).hexdigest()) + + +def Builtin_COALESCE(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-coalesce + """ + for x in expr.get('arg', variables=True): + if x is not None and not isinstance(x, (SPARQLError, Variable)): + return x + raise SPARQLError( + "COALESCE got no arguments that did not evaluate to an error") + + +def Builtin_CEIL(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-ceil + """ + + l = expr.arg + return Literal(int(math.ceil(numeric(l))), datatype=l.datatype) + + +def Builtin_FLOOR(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-floor + """ + l = expr.arg + return Literal(int(math.floor(numeric(l))), datatype=l.datatype) + + +def Builtin_ROUND(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-round + """ + + # This used to be just math.bound + # but in py3k bound was changed to + # "round-to-even" behaviour + # this is an ugly work-around + l = expr.arg + v = numeric(l) + v = int(Decimal(v).quantize(1, ROUND_HALF_UP)) + return Literal(v, datatype=l.datatype) + + +def Builtin_REGEX(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-regex + Invokes the XPath fn:matches function to match text against a regular + expression pattern. + The regular expression language is defined in XQuery 1.0 and XPath 2.0 + Functions and Operators section 7.6.1 Regular Expression Syntax + """ + + text = string(expr.text) + pattern = string(expr.pattern) + flags = expr.flags + + cFlag = 0 + if flags: + # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags) + # to Python's re flags + flagMap = dict( + [('i', re.IGNORECASE), ('s', re.DOTALL), ('m', re.MULTILINE)]) + cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags]) + + return Literal(bool(re.search(str(pattern), text, cFlag))) + + +def Builtin_REPLACE(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-substr + """ + text = string(expr.arg) + pattern = string(expr.pattern) + replacement = string(expr.replacement) + flags = expr.flags + + # python uses \1, xpath/sparql uses $1 + replacement = re.sub('\\$([0-9]*)', r'\\\1', replacement) + + def _r(m): + + # Now this is ugly. + # Python has a "feature" where unmatched groups return None + # then re.sub chokes on this. + # see http://bugs.python.org/issue1519638 , fixed and errs in py3.5 + + # this works around and hooks into the internal of the re module... + + # the match object is replaced with a wrapper that + # returns "" instead of None for unmatched groups + + class _m(): + def __init__(self, m): + self.m = m + self.string = m.string + + def group(self, n): + return m.group(n) or "" + + return re._expand(pattern, _m(m), replacement) + + cFlag = 0 + if flags: + # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags) + # to Python's re flags + flagMap = dict( + [('i', re.IGNORECASE), ('s', re.DOTALL), ('m', re.MULTILINE)]) + cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags]) + + # @@FIXME@@ either datatype OR lang, NOT both + + # this is necessary due to different treatment of unmatched groups in + # python versions. see comments above in _r(m). + compat_r = str(replacement) if sys.version_info[:2] >= (3, 5) else _r + + return Literal(re.sub(str(pattern), compat_r, text, cFlag), + datatype=text.datatype, lang=text.language) + + +def Builtin_STRDT(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strdt + """ + + return Literal(str(expr.arg1), datatype=expr.arg2) + + +def Builtin_STRLANG(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strlang + """ + + s = string(expr.arg1) + if s.language or s.datatype: + raise SPARQLError('STRLANG expects a simple literal') + + # TODO: normalisation of lang tag to lower-case + # should probably happen in literal __init__ + return Literal(str(s), lang=str(expr.arg2).lower()) + + +def Builtin_CONCAT(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-concat + """ + + # dt/lang passed on only if they all match + + dt = set(x.datatype for x in expr.arg) + dt = dt.pop() if len(dt) == 1 else None + + lang = set(x.language for x in expr.arg) + lang = lang.pop() if len(lang) == 1 else None + + return Literal("".join(string(x) + for x in expr.arg), datatype=dt, lang=lang) + + +def _compatibleStrings(a, b): + string(a) + string(b) + + if b.language and a.language != b.language: + raise SPARQLError('incompatible arguments to str functions') + + +def Builtin_STRSTARTS(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strstarts + """ + + a = expr.arg1 + b = expr.arg2 + _compatibleStrings(a, b) + + return Literal(a.startswith(b)) + + +def Builtin_STRENDS(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strends + """ + a = expr.arg1 + b = expr.arg2 + + _compatibleStrings(a, b) + + return Literal(a.endswith(b)) + + +def Builtin_STRBEFORE(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strbefore + """ + + a = expr.arg1 + b = expr.arg2 + _compatibleStrings(a, b) + + i = a.find(b) + if i == -1: + return Literal("") + else: + return Literal(a[:i], lang=a.language, datatype=a.datatype) + + +def Builtin_STRAFTER(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strafter + """ + + a = expr.arg1 + b = expr.arg2 + _compatibleStrings(a, b) + + i = a.find(b) + if i == -1: + return Literal("") + else: + return Literal(a[i + len(b):], lang=a.language, datatype=a.datatype) + + +def Builtin_CONTAINS(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-strcontains + """ + + a = expr.arg1 + b = expr.arg2 + _compatibleStrings(a, b) + + return Literal(b in a) + + +def Builtin_ENCODE_FOR_URI(expr, ctx): + return Literal(urllib.parse.quote(string(expr.arg).encode("utf-8"))) + + +def Builtin_SUBSTR(expr, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-substr + """ + + a = string(expr.arg) + + start = numeric(expr.start) - 1 + + length = expr.length + if length is not None: + length = numeric(length) + start + + return Literal(a[start:length], lang=a.language, datatype=a.datatype) + + +def Builtin_STRLEN(e, ctx): + l = string(e.arg) + + return Literal(len(l)) + + +def Builtin_STR(e, ctx): + arg = e.arg + if isinstance(arg, SPARQLError): + raise arg + return Literal(str(arg)) # plain literal + + +def Builtin_LCASE(e, ctx): + l = string(e.arg) + + return Literal(l.lower(), datatype=l.datatype, lang=l.language) + + +def Builtin_LANGMATCHES(e, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-langMatches + + + """ + langTag = string(e.arg1) + langRange = string(e.arg2) + + if str(langTag) == "": + return Literal(False) # nothing matches empty! + + return Literal(_lang_range_check(langRange, langTag)) + + +def Builtin_NOW(e, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-now + """ + return Literal(ctx.now) + + +def Builtin_YEAR(e, ctx): + d = datetime(e.arg) + return Literal(d.year) + + +def Builtin_MONTH(e, ctx): + d = datetime(e.arg) + return Literal(d.month) + + +def Builtin_DAY(e, ctx): + d = datetime(e.arg) + return Literal(d.day) + + +def Builtin_HOURS(e, ctx): + d = datetime(e.arg) + return Literal(d.hour) + + +def Builtin_MINUTES(e, ctx): + d = datetime(e.arg) + return Literal(d.minute) + + +def Builtin_SECONDS(e, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-seconds + """ + d = datetime(e.arg) + return Literal(d.second, datatype=XSD.decimal) + + +def Builtin_TIMEZONE(e, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-timezone + + :returns: the timezone part of arg as an xsd:dayTimeDuration. + :raises: an error if there is no timezone. + """ + dt = datetime(e.arg) + if not dt.tzinfo: + raise SPARQLError('datatime has no timezone: %r' % dt) + + delta = dt.tzinfo.utcoffset(ctx.now) + + d = delta.days + s = delta.seconds + neg = "" + + if d < 0: + s = -24 * 60 * 60 * d - s + d = 0 + neg = "-" + + h = s / (60 * 60) + m = (s - h * 60 * 60) / 60 + s = s - h * 60 * 60 - m * 60 + + tzdelta = "%sP%sT%s%s%s" % (neg, + "%dD" % d if d else "", + "%dH" % h if h else "", + "%dM" % m if m else "", + "%dS" % s if not d and not h and not m else "") + + return Literal(tzdelta, datatype=XSD.dayTimeDuration) + + +def Builtin_TZ(e, ctx): + d = datetime(e.arg) + if not d.tzinfo: + return Literal("") + n = d.tzinfo.tzname(d) + if n == "UTC": + n = "Z" + return Literal(n) + + +def Builtin_UCASE(e, ctx): + l = string(e.arg) + + return Literal(l.upper(), datatype=l.datatype, lang=l.language) + + +def Builtin_LANG(e, ctx): + + """ + http://www.w3.org/TR/sparql11-query/#func-lang + + Returns the language tag of ltrl, if it has one. It returns "" if ltrl has + no language tag. Note that the RDF data model does not include literals + with an empty language tag. + """ + + l = literal(e.arg) + return Literal(l.language or "") + + +def Builtin_DATATYPE(e, ctx): + l = e.arg + if not isinstance(l, Literal): + raise SPARQLError('Can only get datatype of literal: %r' % l) + if l.language: + return RDF_langString + if not l.datatype and not l.language: + return XSD.string + return l.datatype + + +def Builtin_sameTerm(e, ctx): + a = e.arg1 + b = e.arg2 + return Literal(a == b) + + +def Builtin_BOUND(e, ctx): + """ + http://www.w3.org/TR/sparql11-query/#func-bound + """ + n = e.get('arg', variables=True) + + return Literal(not isinstance(n, Variable)) + + +def Builtin_EXISTS(e, ctx): + # damn... + from rdflib.plugins.sparql.evaluate import evalPart + + exists = e.name == 'Builtin_EXISTS' + + ctx = ctx.ctx.thaw(ctx) # hmm + for x in evalPart(ctx, e.graph): + return Literal(exists) + return Literal(not exists) + + +def Function(e, ctx): + """ + Custom functions (and casts!) + """ + + if e.iri in XSD_DTs: + # a cast + + if not e.expr: + raise SPARQLError("Nothing given to cast.") + if len(e.expr) > 1: + raise SPARQLError("Cannot cast more than one thing!") + + x = e.expr[0] + + if e.iri == XSD.string: + + if isinstance(x, (URIRef, Literal)): + return Literal(x, datatype=XSD.string) + else: + raise SPARQLError( + "Cannot cast term %r of type %r" % (x, type(x))) + + if not isinstance(x, Literal): + raise SPARQLError( + "Can only cast Literals to non-string data-types") + + if x.datatype and not x.datatype in XSD_DTs: + raise SPARQLError( + "Cannot cast literal with unknown datatype: %r" % x.datatype) + + if e.iri == XSD.dateTime: + if x.datatype and x.datatype not in (XSD.dateTime, XSD.string): + raise SPARQLError( + "Cannot cast %r to XSD:dateTime" % x.datatype) + try: + return Literal(isodate.parse_datetime(x), datatype=e.iri) + except: + raise SPARQLError("Cannot interpret '%r' as datetime" % x) + + if x.datatype == XSD.dateTime: + raise SPARQLError("Cannot cast XSD.dateTime to %r" % e.iri) + + if e.iri in (XSD.float, XSD.double): + try: + return Literal(float(x), datatype=e.iri) + except: + raise SPARQLError("Cannot interpret '%r' as float" % x) + + elif e.iri == XSD.decimal: + if "e" in x or "E" in x: # SPARQL/XSD does not allow exponents in decimals + raise SPARQLError("Cannot interpret '%r' as decimal" % x) + try: + return Literal(Decimal(x), datatype=e.iri) + except: + raise SPARQLError("Cannot interpret '%r' as decimal" % x) + + elif e.iri == XSD.integer: + try: + return Literal(int(x), datatype=XSD.integer) + except: + raise SPARQLError("Cannot interpret '%r' as int" % x) + + elif e.iri == XSD.boolean: + # # I would argue that any number is True... + # try: + # return Literal(bool(int(x)), datatype=XSD.boolean) + # except: + if x.lower() in ("1", "true"): + return Literal(True) + if x.lower() in ("0", "false"): + return Literal(False) + + raise SPARQLError("Cannot interpret '%r' as bool" % x) + else: + raise Exception("I do not know how to cast to %r" % e.iri) + + else: + raise SPARQLError('Unknown function %r"%e.iri') + + # TODO: Custom functions! + + +def UnaryNot(expr, ctx): + return Literal(not EBV(expr.expr)) + + +def UnaryMinus(expr, ctx): + return Literal(-numeric(expr.expr)) + + +def UnaryPlus(expr, ctx): + return Literal(+numeric(expr.expr)) + + +def MultiplicativeExpression(e, ctx): + + expr = e.expr + other = e.other + + # because of the way the mul-expr production handled operator precedence + # we sometimes have nothing to do + if other is None: + return expr + try: + res = Decimal(numeric(expr)) + for op, f in zip(e.op, other): + f = numeric(f) + + if type(f) == float: + res = float(res) + + if op == '*': + res *= f + else: + res /= f + except (InvalidOperation, ZeroDivisionError): + raise SPARQLError('divide by 0') + + return Literal(res) + + +def AdditiveExpression(e, ctx): + + expr = e.expr + other = e.other + + # because of the way the add-expr production handled operator precedence + # we sometimes have nothing to do + if other is None: + return expr + + res = numeric(expr) + + dt = expr.datatype + + for op, term in zip(e.op, other): + n = numeric(term) + if isinstance(n, Decimal) and isinstance(res, float): + n = float(n) + if isinstance(n, float) and isinstance(res, Decimal): + res = float(res) + + dt = type_promotion(dt, term.datatype) + + if op == '+': + res += n + else: + res -= n + + return Literal(res, datatype=dt) + + +def RelationalExpression(e, ctx): + + expr = e.expr + other = e.other + op = e.op + + # because of the way the add-expr production handled operator precedence + # we sometimes have nothing to do + if other is None: + return expr + + ops = dict([('>', lambda x, y: x.__gt__(y)), + ('<', lambda x, y: x.__lt__(y)), + ('=', lambda x, y: x.eq(y)), + ('!=', lambda x, y: x.neq(y)), + ('>=', lambda x, y: x.__ge__(y)), + ('<=', lambda x, y: x.__le__(y)), + ('IN', pyop.contains), + ('NOT IN', lambda x, y: not pyop.contains(x, y))]) + + if op in ('IN', 'NOT IN'): + + res = (op == 'NOT IN') + + error = False + + if other == RDF.nil: + other = [] + + for x in other: + try: + if x == expr: + return Literal(True ^ res) + except SPARQLError as e: + error = e + if not error: + return Literal(False ^ res) + else: + raise error + + if not op in ('=', '!=', 'IN', 'NOT IN'): + if not isinstance(expr, Literal): + raise SPARQLError( + "Compare other than =, != of non-literals is an error: %r" % + expr) + if not isinstance(other, Literal): + raise SPARQLError( + "Compare other than =, != of non-literals is an error: %r" % + other) + else: + if not isinstance(expr, Node): + raise SPARQLError('I cannot compare this non-node: %r' % expr) + if not isinstance(other, Node): + raise SPARQLError('I cannot compare this non-node: %r' % other) + + if isinstance(expr, Literal) and isinstance(other, Literal): + + if expr.datatype != None and expr.datatype not in XSD_DTs and other.datatype != None and other.datatype not in XSD_DTs: + # in SPARQL for non-XSD DT Literals we can only do =,!= + if op not in ('=', '!='): + raise SPARQLError( + 'Can only do =,!= comparisons of non-XSD Literals') + + try: + r = ops[op](expr, other) + if r == NotImplemented: + raise SPARQLError('Error when comparing') + except TypeError as te: + raise SPARQLError(*te.args) + return Literal(r) + + +def ConditionalAndExpression(e, ctx): + + # TODO: handle returned errors + + expr = e.expr + other = e.other + + # because of the way the add-expr production handled operator precedence + # we sometimes have nothing to do + if other is None: + return expr + + return Literal(all(EBV(x) for x in [expr] + other)) + + +def ConditionalOrExpression(e, ctx): + + # TODO: handle errors + + expr = e.expr + other = e.other + + # because of the way the add-expr production handled operator precedence + # we sometimes have nothing to do + if other is None: + return expr + # A logical-or that encounters an error on only one branch + # will return TRUE if the other branch is TRUE and an error + # if the other branch is FALSE. + error = None + for x in [expr] + other: + try: + if EBV(x): + return Literal(True) + except SPARQLError as e: + error = e + if error: + raise error + return Literal(False) + + +def not_(arg): + return Expr('UnaryNot', UnaryNot, expr=arg) + + +def and_(*args): + if len(args) == 1: + return args[0] + + return Expr('ConditionalAndExpression', ConditionalAndExpression, + expr=args[0], other=list(args[1:])) + +TrueFilter = Expr('TrueFilter', lambda _1, _2: Literal(True)) + + +def simplify(expr): + if isinstance(expr, ParseResults) and len(expr) == 1: + return simplify(expr[0]) + + if isinstance(expr, (list, ParseResults)): + return list(map(simplify, expr)) + if not isinstance(expr, CompValue): + return expr + if expr.name.endswith('Expression'): + if expr.other is None: + return simplify(expr.expr) + + for k in list(expr.keys()): + expr[k] = simplify(expr[k]) + # expr['expr']=simplify(expr.expr) + # expr['other']=simplify(expr.other) + + return expr + + +def literal(s): + if not isinstance(s, Literal): + raise SPARQLError("Non-literal passed as string: %r" % s) + return s + + +def datetime(e): + if not isinstance(e, Literal): + raise SPARQLError("Non-literal passed as datetime: %r" % e) + if not e.datatype == XSD.dateTime: + raise SPARQLError( + "Literal with wrong datatype passed as datetime: %r" % e) + return e.toPython() + + +def string(s): + """ + Make sure the passed thing is a string literal + i.e. plain literal, xsd:string literal or lang-tagged literal + """ + if not isinstance(s, Literal): + raise SPARQLError("Non-literal passes as string: %r" % s) + if s.datatype and s.datatype != XSD.string: + raise SPARQLError( + "Non-string datatype-literal passes as string: %r" % s) + return s + + +def numeric(expr): + """ + return a number from a literal + http://www.w3.org/TR/xpath20/#promotion + + or TypeError + """ + + if not isinstance(expr, Literal): + raise SPARQLTypeError("%r is not a literal!" % expr) + + if expr.datatype not in (XSD.float, XSD.double, + XSD.decimal, XSD.integer, + XSD.nonPositiveInteger, XSD.negativeInteger, + XSD.nonNegativeInteger, XSD.positiveInteger, + XSD.unsignedLong, XSD.unsignedInt, + XSD.unsignedShort, XSD.unsignedByte, + XSD.long, XSD.int, XSD.short, XSD.byte): + raise SPARQLTypeError("%r does not have a numeric datatype!" % expr) + + return expr.toPython() + + +def EBV(rt): + """ + * If the argument is a typed literal with a datatype of xsd:boolean, + the EBV is the value of that argument. + * If the argument is a plain literal or a typed literal with a + datatype of xsd:string, the EBV is false if the operand value + has zero length; otherwise the EBV is true. + * If the argument is a numeric type or a typed literal with a datatype + derived from a numeric type, the EBV is false if the operand value is + NaN or is numerically equal to zero; otherwise the EBV is true. + * All other arguments, including unbound arguments, produce a type error. + + """ + + if isinstance(rt, Literal): + + if rt.datatype == XSD.boolean: + return rt.toPython() + + elif rt.datatype == XSD.string or rt.datatype is None: + return len(rt) > 0 + + else: + pyRT = rt.toPython() + + if isinstance(pyRT, Literal): + # Type error, see: http://www.w3.org/TR/rdf-sparql-query/#ebv + raise SPARQLTypeError( + "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \ + 'Could not determine the EBV for : %r" % rt) + else: + return bool(pyRT) + + else: + raise SPARQLTypeError( + "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \ + 'Only literals have Boolean values! %r" % rt) + + +def _lang_range_check(range, lang): + """ + Implementation of the extended filtering algorithm, as defined in point + 3.3.2, of U{RFC 4647<http://www.rfc-editor.org/rfc/rfc4647.txt>}, on + matching language ranges and language tags. + Needed to handle the C{rdf:PlainLiteral} datatype. + @param range: language range + @param lang: language tag + @rtype: boolean + + @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">} + + Taken from `RDFClosure/RestrictedDatatype.py`__ + + .. __:http://dev.w3.org/2004/PythonLib-IH/RDFClosure/RestrictedDatatype.py + + """ + def _match(r, l): + """ + Matching of a range and language item: either range is a wildcard + or the two are equal + @param r: language range item + @param l: language tag item + @rtype: boolean + """ + return r == '*' or r == l + + rangeList = range.strip().lower().split('-') + langList = lang.strip().lower().split('-') + if not _match(rangeList[0], langList[0]): + return False + if len(rangeList) > len(langList): + return False + + return all(_match(*x) for x in zip(rangeList, langList))