/* -*- Mode: c; c-basic-offset: 2 -*- * * rasqal_literal.c - Rasqal literals * * $Id: rasqal_literal.c 11551 2006-10-29 21:12:27Z dajobe $ * * Copyright (C) 2003-2006, David Beckett http://purl.org/net/dajobe/ * Copyright (C) 2003-2005, University of Bristol, UK http://www.bristol.ac.uk/ * * This package is Free Software and part of Redland http://librdf.org/ * * It is licensed under the following three licenses as alternatives: * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version * 2. GNU General Public License (GPL) V2 or any newer version * 3. Apache License, V2.0 or any newer version * * You may not use this file except in compliance with at least one of * the above three licenses. * * See LICENSE.html or LICENSE.txt at the top of this package for the * complete terms and further detail along with the license texts for * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. * * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include /* for isnan() */ #include #ifdef RASQAL_REGEX_PCRE #include #endif #ifdef RASQAL_REGEX_POSIX #include #include #endif #include "rasqal.h" #include "rasqal_internal.h" /** * rasqal_new_integer_literal: * @type: Type of literal such as RASQAL_LITERAL_INTEGER or RASQAL_LITERAL_BOOLEAN * @integer: int value * * Constructor - Create a new Rasqal integer literal. * * The integer decimal number is turned into a rasqal integer literal * and given a datatype of xsd:integer * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_integer_literal(rasqal_literal_type type, int integer) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=type; l->value.integer=integer; l->string=(unsigned char*)RASQAL_MALLOC(cstring, 30); /* FIXME */ sprintf((char*)l->string, "%d", integer); l->string_len=strlen((const char*)l->string); l->datatype=raptor_uri_copy(rasqal_xsd_integer_uri); l->usage=1; return l; } /** * rasqal_new_double_literal: * @d: double literal * * Constructor - Create a new Rasqal double literal. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_double_literal(double d) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_DOUBLE; l->value.floating=d; l->string=(unsigned char*)RASQAL_MALLOC(cstring, 30); /* FIXME */ sprintf((char*)l->string, "%1g", d); l->string_len=strlen((const char*)l->string); l->datatype=raptor_uri_copy(rasqal_xsd_double_uri); l->usage=1; return l; } /** * rasqal_new_floating_literal: * @f: floating literal * * @Deprecated: Use rasqal_new_double_literal(). * * Constructor - Create a new Rasqal double literal. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_floating_literal(double f) { return rasqal_new_double_literal(f); } /** * rasqal_new_uri_literal: * @uri: #raptor_uri uri * * Constructor - Create a new Rasqal URI literal from a raptor URI. * * The uri is an input parameter and is stored in the literal, not copied. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_uri_literal(raptor_uri *uri) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_URI; l->value.uri=uri; l->usage=1; return l; } /** * rasqal_new_pattern_literal: * @pattern: regex pattern * @flags: regex flags * * Constructor - Create a new Rasqal pattern literal. * * The pattern and flags are input parameters and are stored in the * literal, not copied. The set of flags recognised depends * on the regex engine and the query language. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_pattern_literal(const unsigned char *pattern, const char *flags) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_PATTERN; l->string=pattern; l->string_len=strlen((const char*)pattern); l->flags=(const unsigned char*)flags; l->usage=1; return l; } /** * rasqal_new_decimal_literal: * @decimal: decimal literal * * Constructor - Create a new Rasqal decimal literal. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_decimal_literal(const unsigned char *decimal) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_DECIMAL; l->string_len=strlen((const char*)decimal); l->string=(unsigned char*)RASQAL_MALLOC(cstring, l->string_len+1); strcpy((char*)l->string, (const char*)decimal); l->datatype=raptor_uri_copy(rasqal_xsd_decimal_uri); l->usage=1; return l; } /* * rasqal_literal_string_to_native - INTERNAL Upgrade a datatyped literal string to an internal typed literal * @l: #rasqal_literal to operate on inline * @error_handler: error handling function * @error_data: data for error handle * * At present this promotes datatyped literals * xsd:integer to RASQAL_LITERAL_INTEGER * xsd:double to RASQAL_LITERAL_DOUBLE * xsd:float to RASQAL_LITERAL_FLOAT * xsd:boolean to RASQAL_LITERAL_BOOLEAN * * Return value: non-0 on failure **/ int rasqal_literal_string_to_native(rasqal_literal *l, raptor_simple_message_handler error_handler, void *error_data) { if(!l->datatype) return 0; if(raptor_uri_equals(l->datatype, rasqal_xsd_integer_uri)) { int i=atoi((const char*)l->string); if(l->language) { RASQAL_FREE(cstring, (void*)l->language); l->language=NULL; } l->type=RASQAL_LITERAL_INTEGER; l->value.integer=i; return 0; } if(raptor_uri_equals(l->datatype, rasqal_xsd_double_uri) || raptor_uri_equals(l->datatype, rasqal_xsd_float_uri)) { double d=0.0; int n; n=sscanf((char*)l->string, "%lf", &d); if(n != 1) { if(error_handler) error_handler(error_data, "Illegal floating point string '%s'", l->string); return 1; } if(l->language) { RASQAL_FREE(cstring, (void*)l->language); l->language=NULL; } l->type= raptor_uri_equals(l->datatype, rasqal_xsd_float_uri) ? RASQAL_LITERAL_FLOAT : RASQAL_LITERAL_DOUBLE; l->value.floating=d; return 0; } if(raptor_uri_equals(l->datatype, rasqal_xsd_boolean_uri)) { int b=0; if(!strcmp((const char*)l->string, "true") || !strcmp((const char*)l->string, "TRUE") || !strcmp((const char*)l->string, "1")) b=1; if(l->language) { RASQAL_FREE(cstring, (void*)l->language); l->language=NULL; } /* static string for boolean */ l->string=b ? RASQAL_XSD_BOOLEAN_TRUE : RASQAL_XSD_BOOLEAN_FALSE; l->string_len=(b ? 4 : 5); l->type=RASQAL_LITERAL_BOOLEAN; l->value.integer=b; return 0; } return 0; } /** * rasqal_new_string_literal: * @string: UTF-8 string lexical form * @language: RDF language (xml:lang) (or NULL) * @datatype: datatype URI (or NULL) * @datatype_qname: datatype qname string (or NULL) * * Constructor - Create a new Rasqal string literal. * * All parameters are input parameters and if present are stored in * the literal, not copied. * * The datatype and datatype_qname parameters are alternatives; the * qname is a datatype that cannot be resolved till later since the * prefixes have not yet been declared or checked. * * If the string literal is datatyped and of certain types recognised * it may be converted to a different literal type by * rasqal_literal_string_to_native. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_string_literal(const unsigned char *string, const char *language, raptor_uri *datatype, const unsigned char *datatype_qname) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); if(datatype && language) { RASQAL_FREE(cstring, (void*)language); language=NULL; } l->type=RASQAL_LITERAL_STRING; l->string=string; l->string_len=strlen((const char*)string); l->language=language; l->datatype=datatype; l->flags=datatype_qname; l->usage=1; if(rasqal_literal_string_to_native(l, NULL, NULL)) { rasqal_free_literal(l); l=NULL; } return l; } /** * rasqal_new_simple_literal: * @type: RASQAL_LITERAL_BLANK or RASQAL_LITERAL_BLANK_QNAME * @string: the UTF-8 string value to store * * Constructor - Create a new Rasqal simple literal. * * The string is an input parameter and is stored in the * literal, not copied. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_simple_literal(rasqal_literal_type type, const unsigned char *string) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=type; l->string=string; l->string_len=strlen((const char*)string); l->usage=1; return l; } /** * rasqal_new_boolean_literal: * @value: non-0 for true, 0 for false * * Constructor - Create a new Rasqal boolean literal. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_boolean_literal(int value) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_BOOLEAN; l->value.integer=value; l->string=value ? RASQAL_XSD_BOOLEAN_TRUE : RASQAL_XSD_BOOLEAN_FALSE; l->string_len=(value ? 4 : 5); l->usage=1; return l; } /** * rasqal_new_variable_literal: * @variable: #rasqal_variable to use * * Constructor - Create a new Rasqal variable literal. * * variable is an input parameter and stored in the literal, not copied. * * Return value: New #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_new_variable_literal(rasqal_variable *variable) { rasqal_literal* l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); l->type=RASQAL_LITERAL_VARIABLE; l->value.variable=variable; l->usage=1; return l; } /** * rasqal_new_literal_from_literal: * @l: #rasqal_literal object to copy * * Copy Constructor - create a new rasqal_literal object from an existing rasqal_literal object. * * Return value: a new #rasqal_literal object or NULL on failure **/ rasqal_literal* rasqal_new_literal_from_literal(rasqal_literal* l) { l->usage++; return l; } /** * rasqal_free_literal: * @l: #rasqal_literal object * * Destructor - destroy an rasqal_literal object. * **/ void rasqal_free_literal(rasqal_literal* l) { if(--l->usage) return; switch(l->type) { case RASQAL_LITERAL_URI: if(l->value.uri) raptor_free_uri(l->value.uri); break; case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_FLOAT: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: if(l->string) RASQAL_FREE(cstring, (void*)l->string); if(l->language) RASQAL_FREE(cstring, (void*)l->language); if(l->datatype) raptor_free_uri(l->datatype); if(l->type == RASQAL_LITERAL_STRING || l->type == RASQAL_LITERAL_PATTERN) { if(l->flags) RASQAL_FREE(cstring, (void*)l->flags); } break; case RASQAL_LITERAL_BOOLEAN: /* static l->string for boolean, does not need freeing */ break; case RASQAL_LITERAL_VARIABLE: /* It is correct that this is not called here * since all variables are shared and owned by * the rasqal_query sequence variables_sequence */ /* rasqal_free_variable(l->value.variable); */ break; case RASQAL_LITERAL_UNKNOWN: default: abort(); } RASQAL_FREE(rasqal_literal, l); } /* * The order here must match that of rasqal_literal_type * in rasqal.h and is significant as rasqal_literal_compare * uses it for type comparisons with the RASQAL_COMPARE_XQUERY * flag. */ static const char* rasqal_literal_type_labels[RASQAL_LITERAL_LAST+1]={ "UNKNOWN", "blank", "uri", "string", "boolean", "integer", "double", "float", "decimal", "datetime", "pattern", "qname", "variable" }; /** * rasqal_literal_print_type: * @l: the #rasqal_literal object * @fh: the #FILE* handle to print to * * Print a string form for a rasqal literal type. * **/ void rasqal_literal_print_type(rasqal_literal* l, FILE* fh) { rasqal_literal_type type; if(!l) { fputs("null", fh); return; } type=l->type; if(type > RASQAL_LITERAL_LAST) type=RASQAL_LITERAL_UNKNOWN; fputs(rasqal_literal_type_labels[(int)type], fh); } /** * rasqal_literal_print: * @l: the #rasqal_literal object * @fh: the #FILE* handle to print to * * Print a Rasqal literal in a debug format. * * The print debug format may change in any release. **/ void rasqal_literal_print(rasqal_literal* l, FILE* fh) { if(!l) { fputs("null", fh); return; } if(l->type != RASQAL_LITERAL_VARIABLE) rasqal_literal_print_type(l, fh); switch(l->type) { case RASQAL_LITERAL_URI: fprintf(fh, "<%s>", raptor_uri_as_string(l->value.uri)); break; case RASQAL_LITERAL_BLANK: fprintf(fh, " %s", l->string); break; case RASQAL_LITERAL_PATTERN: fprintf(fh, "/%s/%s", l->string, l->flags ? (const char*)l->flags : ""); break; case RASQAL_LITERAL_STRING: fputs("(\"", fh); raptor_print_ntriples_string(fh, l->string, '"'); fputc('"', fh); if(l->language) fprintf(fh, "@%s", l->language); if(l->datatype) fprintf(fh, "^^<%s>", raptor_uri_as_string(l->datatype)); fputc(')', fh); break; case RASQAL_LITERAL_QNAME: fprintf(fh, "(%s)", l->string); break; case RASQAL_LITERAL_INTEGER: fprintf(fh, " %d", l->value.integer); break; case RASQAL_LITERAL_BOOLEAN: fprintf(fh, "(%s)", l->string); break; case RASQAL_LITERAL_DOUBLE: fprintf(fh, " %g", l->value.floating); break; case RASQAL_LITERAL_VARIABLE: rasqal_variable_print(l->value.variable, fh); break; case RASQAL_LITERAL_FLOAT: fprintf(fh, " float(%g)", l->value.floating); break; case RASQAL_LITERAL_DECIMAL: fprintf(fh, " decimal(%s)", l->string); break; case RASQAL_LITERAL_DATETIME: fprintf(fh, " datetime(%s)", l->string); break; case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* * rasqal_literal_as_boolean - INTERNAL Return a literal as a boolean value * @l: #rasqal_literal object * @error: pointer to error flag * * Literals are true if not NULL (uris, strings) or zero (0, 0.0). * Otherwise the error flag is set. * * Return value: non-0 if true **/ int rasqal_literal_as_boolean(rasqal_literal* l, int *error) { if(!l) return 0; switch(l->type) { case RASQAL_LITERAL_URI: return (l->value.uri) != NULL; break; case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: return (l->string) != NULL; break; case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: return l->value.integer != 0; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: return l->value.floating != 0.0; break; case RASQAL_LITERAL_VARIABLE: return rasqal_literal_as_boolean(l->value.variable->value, error); break; case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* * rasqal_literal_as_integer - INTERNAL Return a literal as an integer value * @l: #rasqal_literal object * @error: pointer to error flag * * Integers, booleans, double and float literals natural are turned into * integers. If string values are the lexical form of an integer, that is * returned. Otherwise the error flag is set. * * Return value: integer value **/ int rasqal_literal_as_integer(rasqal_literal* l, int *error) { if(!l) return 0; switch(l->type) { case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: return l->value.integer != 0; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: return (int)l->value.floating; break; case RASQAL_LITERAL_STRING: { char *eptr; double d; int v; eptr=NULL; v=(int)strtol((const char*)l->string, &eptr, 10); if((unsigned char*)eptr != l->string && *eptr=='\0') return v; eptr=NULL; d=strtod((const char*)l->string, &eptr); if((unsigned char*)eptr != l->string && *eptr=='\0') return (int)d; } *error=1; return 0; break; case RASQAL_LITERAL_VARIABLE: return rasqal_literal_as_integer(l->value.variable->value, error); break; case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_URI: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: *error=1; return 0; case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* * rasqal_literal_as_floating - INTERNAL Return a literal as a floating value * @l: #rasqal_literal object * @error: pointer to error flag * * Integers, booleans, double and float literals natural are turned into * integers. If string values are the lexical form of an floating, that is * returned. Otherwise the error flag is set. * * Return value: floating value **/ double rasqal_literal_as_floating(rasqal_literal* l, int *error) { if(!l) return 0; switch(l->type) { case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: return (double)l->value.integer; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: return l->value.floating; break; case RASQAL_LITERAL_STRING: { char *eptr=NULL; double d=strtod((const char*)l->string, &eptr); if((unsigned char*)eptr != l->string && *eptr=='\0') return d; } *error=1; return 0.0; break; case RASQAL_LITERAL_VARIABLE: return rasqal_literal_as_integer(l->value.variable->value, error); break; case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_URI: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: *error=1; return 0.0; case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* * rasqal_literal_as_uri - INTERNAL Return a literal as a raptor_uri* * @l: #rasqal_literal object * * Return value: raptor_uri* value or NULL on failure **/ raptor_uri* rasqal_literal_as_uri(rasqal_literal* l) { if(!l) return NULL; if(l->type==RASQAL_LITERAL_URI) return l->value.uri; if(l->type==RASQAL_LITERAL_VARIABLE) return rasqal_literal_as_uri(l->value.variable->value); abort(); return NULL; } /** * rasqal_literal_as_string_flags: * @l: #rasqal_literal object * @flags: comparison flags * @error: pointer to error * * Return the string format of a literal according to flags. * * flag bits affects conversion: * RASQAL_COMPARE_XQUERY: use XQuery conversion rules * * If @error is not NULL, *error is set to non-0 on error * * Return value: pointer to a shared string format of the literal. **/ const unsigned char* rasqal_literal_as_string_flags(rasqal_literal* l, int flags, int *error) { if(!l) return NULL; switch(l->type) { case RASQAL_LITERAL_BOOLEAN: case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_FLOAT: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: return l->string; case RASQAL_LITERAL_URI: if(flags & RASQAL_COMPARE_XQUERY) { if(error) *error=1; return NULL; } return raptor_uri_as_string(l->value.uri); case RASQAL_LITERAL_VARIABLE: return rasqal_literal_as_string_flags(l->value.variable->value, flags, error); case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /** * rasqal_literal_as_string: * @l: #rasqal_literal object * * Return the string format of a literal. * * Return value: pointer to a shared string format of the literal. **/ const unsigned char* rasqal_literal_as_string(rasqal_literal* l) { return rasqal_literal_as_string_flags(l, 0, NULL); } /** * rasqal_literal_as_variable: * @l: #rasqal_literal object * * Get the variable inside a literal. * * Return value: the #rasqal_variable or NULL if the literal is not a variable **/ rasqal_variable* rasqal_literal_as_variable(rasqal_literal* l) { return (l->type == RASQAL_LITERAL_VARIABLE) ? l->value.variable : NULL; } /* turn the sign of the double into an int, for comparison purposes */ static RASQAL_INLINE int double_to_int(double d) { if(d == 0.0) return 0; return (d < 0.0) ? -1 : 1; } /** * rasqal_literal_compare: * @l1: #rasqal_literal first literal * @l2: #rasqal_literal second literal * @flags: comparison flags * @error: pointer to error * * Compare two literals with type promotion. * * The two literals are compared across their range. If the types * are not the same, they are promoted. If one is a double or float, the * other is promoted to double, otherwise for integers, otherwise * to strings (all literals have a string value). * * The comparison returned is as for strcmp, first before second * returns <0. equal returns 0, and first after second returns >0. * For URIs, the string value is used for the comparsion. * * flag bits affects comparisons: * RASQAL_COMPARE_NOCASE: use case independent string comparisons * RASQAL_COMPARE_XQUERY: use XQuery comparison and type promotion rules * * If @error is not NULL, *error is set to non-0 on error * * Return value: <0, 0, or >0 as described above. **/ int rasqal_literal_compare(rasqal_literal* l1, rasqal_literal* l2, int flags, int *error) { rasqal_literal *lits[2]; unsigned int type; int i; int ints[2]; double doubles[2]; const unsigned char* strings[2]; int errori=0; int seen_string=0; int seen_int=0; int seen_double=0; int seen_boolean=0; int seen_numeric=0; *error=0; /* null literals */ if(!l1 || !l2) { /* if either is not null, the comparison fails */ if(l1 || l2) *error=1; return 0; } lits[0]=l1; lits[1]=l2; for(i=0; i<2; i++) { if(lits[i]->type == RASQAL_LITERAL_VARIABLE) { lits[i]=lits[i]->value.variable->value; /* Need to re-check for NULL values */ if(!lits[i]) { /* A null value, so the comparison fails */ RASQAL_DEBUG2("literal %d is a variable with no value\n", i); if(lits[1-i]) *error=1; return 0; } RASQAL_DEBUG3("literal %d is a variable, value is a %s\n", i, rasqal_literal_type_labels[lits[i]->type]); } switch(lits[i]->type) { case RASQAL_LITERAL_URI: break; case RASQAL_LITERAL_DECIMAL: seen_numeric++; strings[i]=lits[i]->string; break; case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_DATETIME: strings[i]=lits[i]->string; seen_string++; break; case RASQAL_LITERAL_BOOLEAN: seen_boolean=1; ints[i]=lits[i]->value.integer; break; case RASQAL_LITERAL_INTEGER: ints[i]=lits[i]->value.integer; seen_int++; seen_numeric++; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: doubles[i]=lits[i]->value.floating; seen_double++; seen_numeric++; break; case RASQAL_LITERAL_VARIABLE: /* this case was dealt with above, retrieving the value */ case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* end for i=0,1 */ /* work out type to aim for */ if(lits[0]->type != lits[1]->type) { RASQAL_DEBUG3("literal 0 type %s. literal 1 type %s\n", rasqal_literal_type_labels[lits[0]->type], rasqal_literal_type_labels[lits[1]->type]); if(flags & RASQAL_COMPARE_XQUERY) { int type0=(int)lits[0]->type; int type1=(int)lits[1]->type; RASQAL_DEBUG3("xquery literal compare types %d vs %d\n", type0, type1); if(seen_numeric != 2) { return type0 - type1; } /* FIXME - promote all numeric to double or int for now */ type=seen_double ? RASQAL_LITERAL_DOUBLE : RASQAL_LITERAL_INTEGER; } else { type=seen_string ? RASQAL_LITERAL_STRING : RASQAL_LITERAL_INTEGER; if((seen_int & seen_double) || (seen_int & seen_string)) type=RASQAL_LITERAL_DOUBLE; if(seen_boolean & seen_string) type=RASQAL_LITERAL_STRING; } } else type=lits[0]->type; /* do promotions */ for(i=0; i<2; i++ ) { if(lits[i]->type == type) continue; switch(type) { case RASQAL_LITERAL_DOUBLE: doubles[i]=rasqal_literal_as_floating(lits[i], &errori); /* failure always means no match */ if(errori) return 1; RASQAL_DEBUG4("promoted literal %d (type %s) to a floating, with value %g\n", i, rasqal_literal_type_labels[lits[i]->type], doubles[i]); break; case RASQAL_LITERAL_INTEGER: ints[i]=rasqal_literal_as_integer(lits[i], &errori); /* failure always means no match */ if(errori) return 1; RASQAL_DEBUG4("promoted literal %d (type %s) to an integer, with value %d\n", i, rasqal_literal_type_labels[lits[i]->type], ints[i]); break; case RASQAL_LITERAL_STRING: strings[i]=rasqal_literal_as_string(lits[i]); RASQAL_DEBUG4("promoted literal %d (type %s) to a string, with value '%s'\n", i, rasqal_literal_type_labels[lits[i]->type], strings[i]); break; case RASQAL_LITERAL_BOOLEAN: ints[i]=rasqal_literal_as_boolean(lits[i], &errori); /* failure always means no match */ if(errori) return 1; RASQAL_DEBUG4("promoted literal %d (type %s) to a boolean, with value %d\n", i, rasqal_literal_type_labels[lits[i]->type], ints[i]); break; default: *error=1; return 0; } } /* check types are promoted */ switch(type) { case RASQAL_LITERAL_URI: return strcmp((const char*)raptor_uri_as_string(lits[0]->value.uri), (const char*)raptor_uri_as_string(lits[1]->value.uri)); case RASQAL_LITERAL_STRING: if(lits[0]->language || lits[1]->language) { /* if either is null, the comparison fails */ if(!lits[0]->language || !lits[1]->language) return 1; if(rasqal_strcasecmp(lits[0]->language,lits[1]->language)) return 1; } if(lits[0]->datatype || lits[1]->datatype) { int result; /* if either is NULL, do not compare */ if(!lits[0]->datatype || !lits[1]->datatype) return lits[0]->datatype ? 1 : -1; result=strcmp((const char*)raptor_uri_as_string(lits[0]->datatype), (const char*)raptor_uri_as_string(lits[1]->datatype)); if(result) return result; } /* FALLTHROUGH */ case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: if(flags & RASQAL_COMPARE_NOCASE) return rasqal_strcasecmp((const char*)strings[0], (const char*)strings[1]); else return strcmp((const char*)strings[0], (const char*)strings[1]); case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: return ints[0] - ints[1]; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: return double_to_int(doubles[0] - doubles[1]); break; default: abort(); } } /** * rasqal_literal_equals: * @l1: #rasqal_literal literal * @l2: #rasqal_literal data literal * * Compare two literals with no type promotion. * * If the l2 data literal value is a boolean, it will match * the string "true" or "false" in the first literal l1. * * Return value: non-0 if equal **/ int rasqal_literal_equals(rasqal_literal* l1, rasqal_literal* l2) { /* null literals */ if(!l1 || !l2) { /* if either is not null, the comparison fails */ return (l1 || l2); } if(l1->type != l2->type) { if(l2->type == RASQAL_LITERAL_BOOLEAN && l1->type == RASQAL_LITERAL_STRING) return !strcmp((const char*)l1->string, (const char*)l2->string); return 0; } switch(l1->type) { case RASQAL_LITERAL_URI: return raptor_uri_equals(l1->value.uri, l2->value.uri); case RASQAL_LITERAL_STRING: if(l1->language || l2->language) { /* if either is null, the comparison fails */ if(!l1->language || !l2->language) return 0; if(rasqal_strcasecmp(l1->language,l2->language)) return 0; } if(l1->datatype || l2->datatype) { /* if either is null, the comparison fails */ if(!l1->datatype || !l2->datatype) return 0; if(!raptor_uri_equals(l1->datatype,l2->datatype)) return 0; } /* FALLTHROUGH */ case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: return !strcmp((const char*)l1->string, (const char*)l2->string); break; case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: return l1->value.integer == l2->value.integer; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: return l1->value.floating == l2->value.floating; break; case RASQAL_LITERAL_VARIABLE: /* both are variables */ return rasqal_literal_equals(l1->value.variable->value, l2->value.variable->value); case RASQAL_LITERAL_UNKNOWN: default: abort(); } } /* * rasqal_literal_expand_qname - INTERNAL Expand any qname in a literal into a URI * @user_data: #rasqal_query cast as void for use with raptor_sequence_foreach * @l: #rasqal_literal literal * * Expands any QName inside the literal using prefixes that are * declared in the query that may not have been present when the * literal was first declared. Intended to be used standalone * as well as with raptor_sequence_foreach which takes a function * signature that this function matches. * * Return value: non-0 on failure **/ int rasqal_literal_expand_qname(void *user_data, rasqal_literal *l) { rasqal_query *rq=(rasqal_query *)user_data; if(l->type == RASQAL_LITERAL_QNAME) { /* expand a literal qname */ raptor_uri *uri=raptor_qname_string_to_uri(rq->namespaces, l->string, l->string_len, (raptor_simple_message_handler)rasqal_query_simple_error, rq); if(!uri) return 1; RASQAL_FREE(cstring, (void*)l->string); l->string=NULL; l->type=RASQAL_LITERAL_URI; l->value.uri=uri; } else if (l->type == RASQAL_LITERAL_STRING) { raptor_uri *uri; if(l->flags) { /* expand a literal string datatype qname */ uri=raptor_qname_string_to_uri(rq->namespaces, l->flags, strlen((const char*)l->flags), (raptor_simple_message_handler)rasqal_query_simple_error, rq); if(!uri) return 1; l->datatype=uri; RASQAL_FREE(cstring, (void*)l->flags); l->flags=NULL; if(l->language && uri) { RASQAL_FREE(cstring, (void*)l->language); l->language=NULL; } if(rasqal_literal_string_to_native(l, (raptor_simple_message_handler)rasqal_query_simple_error, rq)) { rasqal_free_literal(l); return 1; } } } return 0; } /* * rasqal_literal_has_qname - INTERNAL Check if literal has a qname part * @l: #rasqal_literal literal * * Checks if any part ofthe literal has an unexpanded QName. * * Return value: non-0 if a QName is present **/ int rasqal_literal_has_qname(rasqal_literal *l) { return (l->type == RASQAL_LITERAL_QNAME) || (l->type == RASQAL_LITERAL_STRING && (l->flags)); } /** * rasqal_literal_as_node: * @l: #rasqal_literal object * * Turn a literal into a new RDF string, URI or blank literal. * * Return value: the new #rasqal_literal or NULL on failure **/ rasqal_literal* rasqal_literal_as_node(rasqal_literal* l) { raptor_uri *dt_uri=NULL; rasqal_literal* new_l; switch(l->type) { case RASQAL_LITERAL_URI: case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_BLANK: new_l=rasqal_new_literal_from_literal(l); break; case RASQAL_LITERAL_VARIABLE: new_l=l->value.variable->value; if(new_l) return rasqal_new_literal_from_literal(new_l); else return NULL; break; case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: if(l->type == RASQAL_LITERAL_BOOLEAN) dt_uri=raptor_uri_copy(rasqal_xsd_boolean_uri); else dt_uri=raptor_uri_copy(l->datatype); new_l=(rasqal_literal*)RASQAL_CALLOC(rasqal_literal, 1, sizeof(rasqal_literal)); new_l->type=RASQAL_LITERAL_STRING; new_l->string_len=strlen((const char*)l->string); new_l->string=(unsigned char*)RASQAL_MALLOC(cstring, new_l->string_len+1); strcpy((char*)new_l->string, (const char*)l->string); new_l->datatype=dt_uri; new_l->flags=NULL; new_l->usage=1; break; case RASQAL_LITERAL_QNAME: /* QNames should be gone by the time expression eval happens */ case RASQAL_LITERAL_PATTERN: /* FALLTHROUGH */ case RASQAL_LITERAL_UNKNOWN: default: RASQAL_FATAL2("Cannot turn literal type %d into a node", l->type); abort(); } return new_l; } /* * rasqal_literal_ebv - INTERNAL Get the rasqal_literal effective boolean value * @l: #rasqal_literal literal * * Return value: non-0 if EBV is true, else false **/ int rasqal_literal_ebv(rasqal_literal* l) { rasqal_variable* v; /* Result is true unless... */ int b=1; v=rasqal_literal_as_variable(l); if(v) { if(v->value == NULL) { /* ... The operand is unbound */ b=0; goto done; } l=v->value; } if(l->type == RASQAL_LITERAL_BOOLEAN && !l->value.integer) { /* ... The operand is an xs:boolean with a FALSE value. */ b=0; } else if(l->type == RASQAL_LITERAL_STRING && !l->datatype && !l->string_len) { /* ... The operand is a 0-length untyped RDF literal or xs:string. */ b=0; } else if((l->type == RASQAL_LITERAL_INTEGER && !l->value.integer) || ((l->type == RASQAL_LITERAL_DOUBLE || l->type == RASQAL_LITERAL_FLOAT) && !l->value.floating) ) { /* ... The operand is any numeric type with a value of 0. */ /* FIXME - deal with decimal */ b=0; } else if((l->type == RASQAL_LITERAL_DOUBLE || l->type == RASQAL_LITERAL_FLOAT) && isnan(l->value.floating) ) { /* ... The operand is an xs:double or xs:float with a value of NaN */ b=0; } done: return b; } /* * rasqal_literal_Is_constant - INTERNAL Check if a literal is a constant * @l: #rasqal_literal literal * * Return value: non-0 if literal is a constant **/ int rasqal_literal_is_constant(rasqal_literal* l) { switch(l->type) { case RASQAL_LITERAL_URI: case RASQAL_LITERAL_BLANK: case RASQAL_LITERAL_STRING: case RASQAL_LITERAL_PATTERN: case RASQAL_LITERAL_QNAME: case RASQAL_LITERAL_INTEGER: case RASQAL_LITERAL_BOOLEAN: case RASQAL_LITERAL_DOUBLE: case RASQAL_LITERAL_FLOAT: case RASQAL_LITERAL_DECIMAL: case RASQAL_LITERAL_DATETIME: return 1; case RASQAL_LITERAL_VARIABLE: return 0; case RASQAL_LITERAL_UNKNOWN: default: abort(); } } rasqal_formula* rasqal_new_formula(void) { return (rasqal_formula*)RASQAL_CALLOC(rasqal_formula, 1, sizeof(rasqal_formula)); } void rasqal_free_formula(rasqal_formula* formula) { if(formula->triples) raptor_free_sequence(formula->triples); if(formula->value) rasqal_free_literal(formula->value); RASQAL_FREE(rasqal_formula, formula); } void rasqal_formula_print(rasqal_formula* formula, FILE *stream) { fputs("formula(triples=", stream); if(formula->triples) raptor_sequence_print(formula->triples, stream); else fputs("[]", stream); fputs(", value=", stream); if(formula->value) rasqal_literal_print(formula->value, stream); else fputs("NULL", stream); fputc(')', stream); } rasqal_formula* rasqal_formula_join(rasqal_formula* first_formula, rasqal_formula* second_formula) { if(!first_formula && !second_formula) return NULL; if(!first_formula) return second_formula; if(!second_formula) return first_formula; if(first_formula->triples || second_formula->triples) { if(!first_formula->triples) { first_formula->triples=second_formula->triples; second_formula->triples=NULL; } else if(second_formula->triples) raptor_sequence_join(first_formula->triples, second_formula->triples); rasqal_free_formula(second_formula); } return first_formula; }