IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 19892


Ignore:
Timestamp:
Oct 3, 2008, 1:30:27 PM (18 years ago)
Author:
Paul Price
Message:

Allow comparison of arithmetic types with strings: the string is
parsed and compared with the value. In the case of floating-point
types, sexagesimal format is also tried.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/types/psMetadataItemCompare.c

    r19805 r19892  
    44
    55#include <stdio.h>
     6#include <stdlib.h>
    67#include <ctype.h>
    78#include <string.h>
     
    1516#include "psMetadataItemCompare.h"
    1617
     18// Parse a string according to some provided format; check for leftovers
     19#define PARSE_STRING(GOOD, RESULT, STRING, FORMAT) \
     20    bool GOOD = true; \
     21    { \
     22        int read;                       /* Number of characters read */ \
     23        if (sscanf(STRING, "%" FORMAT "%n", &(RESULT), &read) <= 0) { \
     24            GOOD = false; \
     25        } \
     26        if (read < strlen(STRING)) { \
     27            /* Something's left over in the string, so they don't match */ \
     28            GOOD = false; \
     29        } \
     30    }
     31
     32// Parse a string as a sexagesimal value
     33static bool parseSexagesimal(double *value, const char *string)
     34{
     35    int big, medium;                    // Big and medium-sized values
     36    float small;                        // Small value
     37    int read;                           // Number of characters read
     38    if (sscanf(string, "%d:%d:%f%n", &big, &medium, &small, &read) != 3 &&
     39        sscanf(string, "%d %d %f%n", &big, &medium, &small, &read) != 3) {
     40        return false;
     41    }
     42    if (read < strlen(string)) {
     43        // Something's left over in the string, so they don't match
     44        return false;
     45    }
     46    *value = abs(big) + (float)medium/60.0 + small/3600.0;
     47    if (big < 0) {
     48        *value *= -1.0;
     49    }
     50    return true;
     51}
     52
     53// Parse a string as a boolean value
     54static bool parseBool(bool *value, const char *string)
     55{
     56    if (strcasecmp(string, "T") == 0 || strcasecmp(string, "TRUE") == 0 || strcmp(string, "1") == 0) {
     57        *value = true;
     58        return true;
     59    }
     60    if (strcasecmp(string, "F") == 0 || strcasecmp(string, "FALSE") == 0 || strcmp(string, "0") == 0) {
     61        *value = false;
     62        return true;
     63    }
     64    return false;
     65}
     66
     67
     68// Operations for comparison
    1769typedef enum {
    18     PS_MD_OP_EQ,
    19     PS_MD_OP_LT,
    20     PS_MD_OP_LE,
    21     PS_MD_OP_GT,
    22     PS_MD_OP_GE,
    23     PS_MD_OP_NE
    24 } psMetadataItemCompareOp;
     70    COMPARE_OP_EQ,                      // Equality
     71    COMPARE_OP_LT,                      // Less than
     72    COMPARE_OP_LE,                      // Less than or equal
     73    COMPARE_OP_GT,                      // Greater than
     74    COMPARE_OP_GE,                      // Greater than or equal
     75    COMPARE_OP_NE                       // Not equal
     76} compareOp;
    2577
    2678/* determine boolean operator specified in comment: OP: XX (default is ==) */
    27 psMetadataItemCompareOp p_psMetadataItemCompareGetOp (const psMetadataItem *item) {
    28 
    29     if (!item->comment) return PS_MD_OP_EQ;
    30 
    31     char *p1 = strstr (item->comment, "OP:");
    32     if (!p1) return PS_MD_OP_EQ;
     79compareOp getCompareOp(const psMetadataItem *item) {
     80
     81    if (!item->comment) {
     82        return COMPARE_OP_EQ;
     83    }
     84
     85    char *p1 = strstr(item->comment, "OP:");
     86    if (!p1) {
     87        return COMPARE_OP_EQ;
     88    }
    3389    p1 += 3; // point to first char after @OP:
    34     while (isblank(p1[0])) p1++;
     90    while (isblank(p1[0])) {
     91        p1++;
     92    }
    3593
    3694    // the string starting at p1 must contain one of the following:
    37     // == or = PS_MD_OP_EQ,
    38     // < PS_MD_OP_LT,
    39     // <= PS_MD_OP_LE,
    40     // > PS_MD_OP_GT,
    41     // >= PS_MD_OP_GE,
    42     // ! PS_MD_OP_NE
    43    
     95    // == or = COMPARE_OP_EQ,
     96    // < COMPARE_OP_LT,
     97    // <= COMPARE_OP_LE,
     98    // > COMPARE_OP_GT,
     99    // >= COMPARE_OP_GE,
     100    // ! COMPARE_OP_NE
     101
    44102    // XXX a bit crude: does not catch the case of invalid chars after boolean op
    45     if (!strncmp (p1, "==", 2)) return PS_MD_OP_EQ;
    46     if (!strncmp (p1, "=",  1)) return PS_MD_OP_EQ;
    47     if (!strncmp (p1, "<=", 2)) return PS_MD_OP_LE;
    48     if (!strncmp (p1, "<",  1)) return PS_MD_OP_LT;
    49     if (!strncmp (p1, ">=", 2)) return PS_MD_OP_GE;
    50     if (!strncmp (p1, ">",  1)) return PS_MD_OP_GT;
    51 
    52     return PS_MD_OP_EQ;
     103    if (!strncmp (p1, "==", 2)) return COMPARE_OP_EQ;
     104    if (!strncmp (p1, "=",  1)) return COMPARE_OP_EQ;
     105    if (!strncmp (p1, "<=", 2)) return COMPARE_OP_LE;
     106    if (!strncmp (p1, "<",  1)) return COMPARE_OP_LT;
     107    if (!strncmp (p1, ">=", 2)) return COMPARE_OP_GE;
     108    if (!strncmp (p1, ">",  1)) return COMPARE_OP_GT;
     109
     110    return COMPARE_OP_EQ;
    53111}
    54112
    55113// XXX better value for tolerance?
    56 # define EQ_TOL 1e-6
    57 
    58 # define COMPARE_CASE(TEMPLATENAME, TEMPLATETYPE, COMPARENAME, COMPARETYPENAME) \
    59   case PS_TYPE_##COMPARETYPENAME: {                                      \
    60       TEMPLATETYPE valueC = (TEMPLATETYPE)compare->data.COMPARENAME;     \
    61       TEMPLATETYPE valueT = (TEMPLATETYPE)template->data.TEMPLATENAME;                    \
    62       /* XXX check the validiy of the type casting? */                 \
    63       if (valueC != compare->data.COMPARENAME) {                         \
    64           return false;                                          \
    65       }                                                          \
    66       /* does template specify a boolean operation in comment? */ \
    67       psMetadataItemCompareOp op = p_psMetadataItemCompareGetOp (template);  \
    68       switch (op) { \
    69         case PS_MD_OP_EQ: \
    70           if ((template->type == PS_TYPE_F32) || (template->type == PS_TYPE_F64)) { \
    71               result = fabs(valueT - valueC) < EQ_TOL; \
    72           } else { \
    73               result = valueT == valueC; \
    74           } \
    75           break; \
    76         case PS_MD_OP_LT: \
    77           result = valueC < valueT; \
    78           break; \
    79         case PS_MD_OP_LE: \
    80           result = valueC <= valueT; \
    81           break; \
    82         case PS_MD_OP_GT: \
    83           result = valueC > valueT; \
    84           break; \
    85         case PS_MD_OP_GE: \
    86           result = valueC >= valueT; \
    87           break; \
    88         case PS_MD_OP_NE: \
    89           result = valueC != valueT; \
    90           break; \
    91         default: \
    92           psAbort ("all cases should have been handled..."); \
     114#define EQ_TOL 1e-6                     // Tolerance for equality
     115
     116// Compare values directly
     117#define COMPARE_VALUES(TEMPLATE, COMPARE, FLOATINGPOINT) { \
     118     /* does template specify a boolean operation in comment? */ \
     119     compareOp op = getCompareOp(template);  \
     120     switch (op) { \
     121       case COMPARE_OP_EQ: \
     122         if (FLOATINGPOINT) { \
     123             return fabs(TEMPLATE - COMPARE) < EQ_TOL; \
     124         } else { \
     125             return TEMPLATE == COMPARE; \
     126         } \
     127       case COMPARE_OP_LT: \
     128         return COMPARE < TEMPLATE; \
     129       case COMPARE_OP_LE: \
     130         return COMPARE <= TEMPLATE; \
     131       case COMPARE_OP_GT: \
     132         return COMPARE > TEMPLATE; \
     133       case COMPARE_OP_GE: \
     134         return COMPARE >= TEMPLATE; \
     135       case COMPARE_OP_NE: \
     136         return COMPARE != TEMPLATE; \
     137       default: \
     138         psAbort("all cases should have been handled..."); \
     139    } \
     140}
     141
     142#define COMPARE_NUMERICAL_CASE(TEMPLATENAME, TEMPLATETYPE, COMPARENAME, COMPARETYPENAME) \
     143  case PS_TYPE_##COMPARETYPENAME: { \
     144      TEMPLATETYPE valueC = (TEMPLATETYPE)compare->data.COMPARENAME; \
     145      TEMPLATETYPE valueT = (TEMPLATETYPE)template->data.TEMPLATENAME; \
     146      /* XXX check the validiy of the type casting? */ \
     147      if (valueC != compare->data.COMPARENAME) { \
     148          return false; \
    93149      } \
    94       return (result); \
    95     }
    96 
    97 #define TEMPLATE_CASE(TYPENAME, NAME, TYPE)                             \
    98     case PS_TYPE_##TYPENAME:                                            \
    99     switch(compare->type) {                                             \
    100         COMPARE_CASE(NAME, TYPE, B  , BOOL);                            \
    101         COMPARE_CASE(NAME, TYPE, U8 , U8 );                             \
    102         COMPARE_CASE(NAME, TYPE, U16, U16);                             \
    103         COMPARE_CASE(NAME, TYPE, U32, U32);                             \
    104         COMPARE_CASE(NAME, TYPE, S8 , S8 );                             \
    105         COMPARE_CASE(NAME, TYPE, S16, S16);                             \
    106         COMPARE_CASE(NAME, TYPE, S32, S32);                             \
    107         COMPARE_CASE(NAME, TYPE, F32, F32);                             \
    108         COMPARE_CASE(NAME, TYPE, F64, F64);                             \
    109       default:                                                          \
     150      COMPARE_VALUES(valueT, valueC, (template->type == PS_TYPE_F32 || template->type == PS_TYPE_F64)); \
     151      psAbort("Should never reach here."); \
     152  }
     153
     154// Compare a string template with an int
     155#define COMPARE_STRING_INT_CASE(TYPE, FORMAT) \
     156  case PS_TYPE_##TYPE: { \
     157    ps##TYPE valueT; \
     158    PARSE_STRING(status, valueT, template->data.V, FORMAT); \
     159    if (!status) { \
     160        return false; \
     161    } \
     162    ps##TYPE valueC = compare->data.TYPE; \
     163    COMPARE_VALUES(valueT, valueC, false); \
     164}
     165
     166
     167// Compare a boolean template with a string
     168#define COMPARE_BOOL_STRING(TYPE, FORMAT) { \
     169    bool valueC; \
     170    if (!parseBool(&valueC, compare->data.V)) { \
     171        return false; \
     172    } \
     173    return (template->data.B == valueC); \
     174}
     175
     176// Compare an integer template with a string
     177#define COMPARE_INT_STRING(TYPE, FORMAT) { \
     178    ps##TYPE valueC; \
     179    PARSE_STRING(status, valueC, compare->data.V, FORMAT); \
     180    if (!status) { \
     181        return false; \
     182    } \
     183    ps##TYPE valueT = template->data.TYPE; \
     184    COMPARE_VALUES(valueT, valueC, false); \
     185}
     186
     187// Compare a float template with a string
     188#define COMPARE_FLOAT_STRING(TYPE, FORMAT) { \
     189    ps##TYPE valueC; \
     190    PARSE_STRING(status, valueC, compare->data.V, FORMAT); \
     191    if (!status) { \
     192        double sexValue; /* Sexagesimal value; just in case the type is not F32 */ \
     193        if (parseSexagesimal(&sexValue, compare->data.V)) { \
     194            valueC = sexValue; \
     195        } else { \
     196            return false; \
     197        } \
     198    } \
     199    ps##TYPE valueT = template->data.TYPE; \
     200    COMPARE_VALUES(valueT, valueC, true); \
     201}
     202
     203#define TEMPLATE_CASE(TYPENAME, NAME, TYPE, COMPARESTRING, FORMAT) \
     204    case PS_TYPE_##TYPENAME: \
     205    switch(compare->type) { \
     206        COMPARE_NUMERICAL_CASE(NAME, TYPE, B  , BOOL); \
     207        COMPARE_NUMERICAL_CASE(NAME, TYPE, U8 , U8 ); \
     208        COMPARE_NUMERICAL_CASE(NAME, TYPE, U16, U16); \
     209        COMPARE_NUMERICAL_CASE(NAME, TYPE, U32, U32); \
     210        COMPARE_NUMERICAL_CASE(NAME, TYPE, U64, U64); \
     211        COMPARE_NUMERICAL_CASE(NAME, TYPE, S8 , S8 ); \
     212        COMPARE_NUMERICAL_CASE(NAME, TYPE, S16, S16); \
     213        COMPARE_NUMERICAL_CASE(NAME, TYPE, S32, S32); \
     214        COMPARE_NUMERICAL_CASE(NAME, TYPE, F32, F32); \
     215        COMPARE_NUMERICAL_CASE(NAME, TYPE, F64, F64); \
     216      case PS_DATA_STRING: { \
     217          if (!template->data.V) { \
     218              return false; \
     219          } \
     220          COMPARESTRING(TYPENAME, FORMAT); \
     221      } \
     222      default: \
    110223        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n", \
    111                 compare->type, template->type);                         \
    112         return false;                                                   \
     224                compare->type, template->type); \
     225        return false; \
    113226    }
    114227
     
    117230                           const psMetadataItem *template) // The template
    118231{
    119 
    120232    // First order checks:
    121233
     
    130242    }
    131243
    132     bool result = false;
    133 
    134244    switch (template->type) {
    135         TEMPLATE_CASE(BOOL, B,   bool) ;
    136         TEMPLATE_CASE(U8,   U8,  psU8 );
    137         TEMPLATE_CASE(U16,  U16, psU16);
    138         TEMPLATE_CASE(U32,  U32, psU32);
    139         TEMPLATE_CASE(S8,   S8,  psS8 );
    140         TEMPLATE_CASE(S16,  S16, psS16);
    141         TEMPLATE_CASE(S32,  S32, psS32);
    142         TEMPLATE_CASE(F32,  F32, psF32);
    143         TEMPLATE_CASE(F64,  F64, psF64);
    144 
    145     case PS_DATA_STRING:
    146 
    147         // for MULTI, try each one & succeed if any match (valid = true is default state)
    148         if (compare->type == PS_DATA_METADATA_MULTI) {
    149             for (int j = 0; j < compare->data.list->n; j++) {
    150                 psMetadataItem *entry = psListGet (compare->data.list, j);
    151                 if (!entry) continue;
    152                 if (entry->type != PS_DATA_STRING) {
    153                   continue;
    154                 }
    155                 if (template->data.V && !entry->data.V) { // expecting valid data, found NULL
    156                   continue;
    157                 }
    158                 if (!template->data.V && entry->data.V) { // expecting NULL, found valid data
    159                   continue;
    160                 }
    161                 // XXX should we return true for both NULL?
    162                 if (!template->data.V && !entry->data.V) { // expecting NULL, found NULL
    163                   return true;
    164                 }
    165                 if (!strcasecmp(entry->data.V, template->data.V)) {
    166                   return true;
    167                 }
    168                 // XXX this is a hack : also compare with the comment field (for HISTORY, COMMENT)
    169                 if (!strcasecmp(entry->comment, template->data.V)) {
    170                   return true;
    171                 }
     245        TEMPLATE_CASE(BOOL, B,   bool,  COMPARE_BOOL_STRING, "");
     246        TEMPLATE_CASE(U8,   U8,  psU8,  COMPARE_INT_STRING,  SCNu8);
     247        TEMPLATE_CASE(U16,  U16, psU16, COMPARE_INT_STRING,  SCNu16);
     248        TEMPLATE_CASE(U32,  U32, psU32, COMPARE_INT_STRING,  SCNu32);
     249        TEMPLATE_CASE(U64,  U64, psU64, COMPARE_INT_STRING,  SCNu64);
     250        TEMPLATE_CASE(S8,   S8,  psS8,  COMPARE_INT_STRING,  SCNd8);
     251        TEMPLATE_CASE(S16,  S16, psS16, COMPARE_INT_STRING,  SCNd16);
     252        TEMPLATE_CASE(S32,  S32, psS32, COMPARE_INT_STRING,  SCNd32);
     253        TEMPLATE_CASE(S64,  S64, psS64, COMPARE_INT_STRING,  SCNd64);
     254        TEMPLATE_CASE(F32,  F32, psF32, COMPARE_FLOAT_STRING, "f");
     255        TEMPLATE_CASE(F64,  F64, psF64, COMPARE_FLOAT_STRING, "lf");
     256      case PS_DATA_STRING: {
     257          switch (compare->type) {
     258            case PS_DATA_STRING: {
     259                psTrace("psLib.types", 10, "Comparing '%s' with '%s'\n",
     260                        compare->data.str, template->data.str);
     261                if ((!compare->data.V && template->data.V) || (!template->data.V && compare->data.V)) {
     262                    return false;
     263                }
     264                return (strcasecmp(compare->data.V, template->data.V) == 0) ? true : false;
    172265            }
    173             return false;
    174         }
    175 
    176         // any other (non-MULTI) is a mis-match
    177         if (compare->type != PS_DATA_STRING) {
    178             return false;
    179         }
    180         psTrace("psLib.types", 10, "Comparing '%s' with '%s'\n", compare->data.str, template->data.str);
    181         if ((!compare->data.V && template->data.V) || (!template->data.V && compare->data.V)) {
    182             return false;
    183         }
    184         return (strcasecmp(compare->data.V, template->data.V) == 0) ? true : false;
    185 
    186     default:
     266            case PS_TYPE_BOOL: {
     267                bool templateValue;
     268                if (!parseBool(&templateValue, template->data.V)) {
     269                    return false;
     270                }
     271                return templateValue == compare->data.B;
     272            }
     273              COMPARE_STRING_INT_CASE(U8,  SCNu8);
     274              COMPARE_STRING_INT_CASE(U16, SCNu16);
     275              COMPARE_STRING_INT_CASE(U32, SCNu32);
     276              COMPARE_STRING_INT_CASE(U64, SCNu64);
     277              COMPARE_STRING_INT_CASE(S8,  SCNd8);
     278              COMPARE_STRING_INT_CASE(S16, SCNd16);
     279              COMPARE_STRING_INT_CASE(S32, SCNd32);
     280              COMPARE_STRING_INT_CASE(S64, SCNd64);
     281            case PS_TYPE_F32:
     282            case PS_TYPE_F64: {
     283                // Check for sexagesimal formating
     284                if (!template->data.V) {
     285                    return false;
     286                }
     287                double templateValue; // Value of template
     288                // Attempt to read the string first as a plain floating-point value, then as sexagesimal
     289                PARSE_STRING(status, templateValue, template->data.V, "lf");
     290                if (!status && !parseSexagesimal(&templateValue, template->data.V)) {
     291                    return false;
     292                }
     293                double compareValue = compare->type == PS_TYPE_F32 ? compare->data.F32 : compare->data.F64;
     294                COMPARE_VALUES(templateValue, compareValue, true);
     295            }
     296            case PS_DATA_METADATA_MULTI: {
     297                // for MULTI, try each one & succeed if any match (valid = true is default state)
     298                for (int j = 0; j < compare->data.list->n; j++) {
     299                    psMetadataItem *entry = psListGet(compare->data.list, j);
     300                    if (!entry) {
     301                        continue;
     302                    }
     303                    if (entry->type != PS_DATA_STRING) {
     304                        continue;
     305                    }
     306                    if (template->data.V && !entry->data.V) { // expecting valid data, found NULL
     307                        continue;
     308                    }
     309                    if (!template->data.V && entry->data.V) { // expecting NULL, found valid data
     310                        continue;
     311                    }
     312                    // XXX should we return true for both NULL?
     313                    if (!template->data.V && !entry->data.V) { // expecting NULL, found NULL
     314                        return true;
     315                    }
     316                    if (!strcasecmp(entry->data.V, template->data.V)) {
     317                        return true;
     318                    }
     319                    // XXX this is a hack : also compare with the comment field (for HISTORY, COMMENT)
     320                    if (!strcasecmp(entry->comment, template->data.V)) {
     321                        return true;
     322                    }
     323                }
     324                return false;
     325            }
     326            default:
     327              // any other type is a mis-match against a string
     328              return false;
     329          }
     330      }
     331      default:
    187332        // Simply don't know how to compare more complex types.
    188333        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Don't know how to compare types %x and %x\n",
Note: See TracChangeset for help on using the changeset viewer.