1 /**********************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  OGC Filter Encoding implementation
6  * Author:   Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
7  *
8  **********************************************************************
9  * Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  ****************************************************************************/
28 
29 #include "mapogcfilter.h"
30 #include "mapserver.h"
31 #include "mapows.h"
32 #include "mapowscommon.h"
33 #include "cpl_minixml.h"
34 
35 #include <string>
36 
FLTGetIsLikeComparisonCommonExpression(FilterEncodingNode * psFilterNode)37 static std::string FLTGetIsLikeComparisonCommonExpression(FilterEncodingNode *psFilterNode)
38 {
39   /* From http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04 */
40   /* also add double quote because we are within a string */
41   const char* pszRegexSpecialCharsAndDoubleQuote = "\\^${[().*+?|\"";
42 
43   if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode || !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
44     return std::string();
45 
46   const FEPropertyIsLike* propIsLike = (const FEPropertyIsLike *)psFilterNode->pOther;
47   const char* pszWild = propIsLike->pszWildCard;
48   const char* pszSingle = propIsLike->pszSingleChar;
49   const char* pszEscape = propIsLike->pszEscapeChar;
50   const bool bCaseInsensitive = propIsLike->bCaseInsensitive != 0;
51 
52   if (!pszWild || strlen(pszWild) == 0 || !pszSingle || strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
53     return std::string();
54 
55   /* -------------------------------------------------------------------- */
56   /*      Use operand with regular expressions.                           */
57   /* -------------------------------------------------------------------- */
58   std::string expr("(\"[");
59 
60   /* attribute */
61   expr += psFilterNode->psLeftNode->pszValue;
62 
63   /* #3521 */
64   if (bCaseInsensitive )
65     expr += "]\" ~* \"";
66   else
67     expr += "]\" ~ \"";
68 
69   const char* pszValue = psFilterNode->psRightNode->pszValue;
70   const size_t nLength = strlen(pszValue);
71 
72   if (nLength > 0) {
73     expr += '^';
74   }
75   for (size_t i=0; i<nLength; i++) {
76     if (pszValue[i] == pszSingle[0]) {
77       expr += '.';
78     /* The Filter escape character is supposed to only escape the single, wildcard and escape character */
79     } else if (pszValue[i] == pszEscape[0] && (
80                     pszValue[i+1] == pszSingle[0] ||
81                     pszValue[i+1] == pszWild[0] ||
82                     pszValue[i+1] == pszEscape[0])) {
83       if( pszValue[i+1] == '\\' )
84       {
85           /* Tricky case: \ must be escaped ncce in the regular expression context
86              so that the regexp matches it as an ordinary character.
87              But as \ is also the escape character for MapServer string, we
88              must escape it again. */
89           expr += "\\" "\\" "\\" "\\";
90       }
91       else
92       {
93         /* If the escaped character is itself a regular expression special character */
94         /* we need to regular-expression-escape-it ! */
95         if( strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i+1]) )
96         {
97             expr += '\\';
98         }
99         expr += pszValue[i+1];
100       }
101       i++;
102     } else if (pszValue[i] == pszWild[0]) {
103       expr += ".*";
104     }
105     /* Escape regular expressions special characters and double quote */
106     else if (strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i]))
107     {
108       if( pszValue[i] == '\\' )
109       {
110           /* See above explantation */
111           expr += "\\" "\\" "\\" "\\";
112       }
113       else
114       {
115         expr += '\\';
116         expr += pszValue[i];
117       }
118     }
119     else {
120       expr += pszValue[i];
121     }
122   }
123   if (nLength > 0) {
124     expr += '$';
125   }
126   expr += "\")";
127   return expr;
128 }
129 
FLTGetIsBetweenComparisonCommonExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)130 static std::string FLTGetIsBetweenComparisonCommonExpresssion(FilterEncodingNode *psFilterNode, layerObj *lp)
131 {
132   if (!psFilterNode || !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
133     return std::string();
134 
135   if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL )
136     return std::string();
137 
138   /* -------------------------------------------------------------------- */
139   /*      Get the bounds value which are stored like boundmin;boundmax    */
140   /* -------------------------------------------------------------------- */
141   int nBounds = 0;
142   char** aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
143   if (nBounds != 2) {
144     msFreeCharArray(aszBounds, nBounds);
145     return std::string();
146   }
147 
148   /* -------------------------------------------------------------------- */
149   /*      check if the value is a numeric value or alphanumeric. If it    */
150   /*      is alphanumeric, add quotes around attribute and values.        */
151   /* -------------------------------------------------------------------- */
152   bool bString = false;
153   bool bDateTime = false;
154   if (aszBounds[0]) {
155     const char* pszType = msOWSLookupMetadata(&(lp->metadata), "OFG",
156         (std::string(psFilterNode->psLeftNode->pszValue)+ "_type").c_str());
157     if (pszType != NULL && (strcasecmp(pszType, "Character") == 0))
158       bString = true;
159     else if (pszType != NULL && (strcasecmp(pszType, "Date") == 0))
160       bDateTime = true;
161     else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
162       bString = true;
163   }
164   if (!bString && !bDateTime) {
165     if (aszBounds[1]) {
166       if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
167         bString = true;
168     }
169   }
170 
171   std::string expr;
172   /* -------------------------------------------------------------------- */
173   /*      build expresssion.                                              */
174   /* -------------------------------------------------------------------- */
175   /* attribute */
176   if (bString)
177     expr += "(\"[";
178   else
179     expr += "([";
180 
181   expr += psFilterNode->psLeftNode->pszValue;
182 
183   if (bString)
184     expr += "]\" ";
185   else
186     expr += "] ";
187 
188   expr += " >= ";
189 
190   if (bString) {
191     expr += '\"';
192   }
193   else if (bDateTime) {
194     expr += '`';
195   }
196 
197   {
198       char* pszTmpEscaped = msStringEscape(aszBounds[0]);
199       expr += pszTmpEscaped;
200       if(pszTmpEscaped != aszBounds[0] ) msFree(pszTmpEscaped);
201   }
202 
203   if (bString) {
204     expr += '\"';
205   }
206   else if (bDateTime) {
207     expr += '`';
208   }
209 
210   expr += " AND ";
211 
212   if (bString)
213     expr += " \"[";
214   else
215     expr += " [";
216 
217   /* attribute */
218   expr += psFilterNode->psLeftNode->pszValue;
219 
220   if (bString)
221     expr += "]\" ";
222   else
223     expr += "] ";
224 
225   expr += " <= ";
226 
227   if (bString) {
228     expr += '\"';
229   }
230   else if (bDateTime) {
231     expr += '`';
232   }
233 
234   {
235       char* pszTmpEscaped = msStringEscape(aszBounds[1]);
236       expr += pszTmpEscaped;
237       if(pszTmpEscaped != aszBounds[1] ) msFree(pszTmpEscaped);
238   }
239 
240   if (bString) {
241     expr += '\"';
242   }
243   else if (bDateTime) {
244     expr += '`';
245   }
246   expr += ')';
247 
248   msFreeCharArray(aszBounds, nBounds);
249 
250   return expr;
251 }
252 
FLTGetBinaryComparisonCommonExpression(FilterEncodingNode * psFilterNode,layerObj * lp)253 char *FLTGetBinaryComparisonCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
254 {
255   char szTmp[1024];
256   char *pszExpression = NULL, *pszTmpEscaped;
257   int bString;
258   int bDateTime;
259 
260   if (psFilterNode == NULL)
261     return NULL;
262 
263   /* -------------------------------------------------------------------- */
264   /*      check if the value is a numeric value or alphanumeric. If it    */
265   /*      is alphanumeric, add quotes around attribute and values.        */
266   /* -------------------------------------------------------------------- */
267   bString = 0;
268   bDateTime = 0;
269   if (psFilterNode->psRightNode->pszValue) {
270     const char* pszType;
271     snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
272     pszType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
273     if (pszType!= NULL && (strcasecmp(pszType, "Character") == 0))
274       bString = 1;
275     else if (pszType!= NULL && (strcasecmp(pszType, "Date") == 0))
276       bDateTime = 1;
277     else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
278       bString = 1;
279   }
280 
281   /* specical case to be able to have empty strings in the expression. */
282   /* propertyislike is always treated as string */
283   if (psFilterNode->psRightNode->pszValue == NULL || strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
284     bString = 1;
285 
286   /* attribute */
287   if (bString)
288     sprintf(szTmp, "%s", "(\"[");
289   else
290     sprintf(szTmp,  "%s","([");
291   pszExpression = msStringConcatenate(pszExpression, szTmp);
292   pszExpression = msStringConcatenate(pszExpression, psFilterNode->psLeftNode->pszValue);
293 
294   if (bString)
295     sprintf(szTmp,  "%s","]\" ");
296   else
297     sprintf(szTmp,  "%s", "] ");
298   pszExpression = msStringConcatenate(pszExpression, szTmp);
299 
300   if (strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0) {
301     /* case insensitive set ? */
302     if (psFilterNode->psRightNode->pOther && (*(int *)psFilterNode->psRightNode->pOther) == 1)
303       sprintf(szTmp,  "%s", "=*");
304     else
305       sprintf(szTmp,  "%s", "=");
306   } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsNotEqualTo") == 0)
307     sprintf(szTmp,  "%s", "!=");
308   else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThan") == 0)
309     sprintf(szTmp,  "%s", "<");
310   else if (strcasecmp(psFilterNode->pszValue, "PropertyIsGreaterThan") == 0)
311     sprintf(szTmp,  "%s", ">");
312   else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThanOrEqualTo") == 0)
313     sprintf(szTmp,  "%s", "<=");
314   else if (strcasecmp(psFilterNode->pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
315     sprintf(szTmp,  "%s", ">=");
316   else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
317     sprintf(szTmp,  "%s", "~");
318 
319   pszExpression = msStringConcatenate(pszExpression, szTmp);
320   pszExpression = msStringConcatenate(pszExpression, " ");
321 
322   /* value */
323   if (bString) {
324     sprintf(szTmp,  "%s", "\"");
325     pszExpression = msStringConcatenate(pszExpression, szTmp);
326   }
327   else if (bDateTime) {
328     sprintf(szTmp,  "%s", "`");
329     pszExpression = msStringConcatenate(pszExpression, szTmp);
330   }
331 
332   if (psFilterNode->psRightNode->pszValue) {
333     pszTmpEscaped = msStringEscape(psFilterNode->psRightNode->pszValue);
334     pszExpression = msStringConcatenate(pszExpression, pszTmpEscaped);
335     if(pszTmpEscaped != psFilterNode->psRightNode->pszValue ) msFree(pszTmpEscaped);
336   }
337 
338   if (bString) {
339     sprintf(szTmp,  "%s", "\"");
340     pszExpression = msStringConcatenate(pszExpression, szTmp);
341   }
342   else if (bDateTime) {
343     sprintf(szTmp,  "%s", "`");
344     pszExpression = msStringConcatenate(pszExpression, szTmp);
345   }
346 
347   sprintf(szTmp,  "%s", ")");
348   pszExpression = msStringConcatenate(pszExpression, szTmp);
349 
350   return pszExpression;
351 }
352 
FLTGetLogicalComparisonCommonExpression(FilterEncodingNode * psFilterNode,layerObj * lp)353 char *FLTGetLogicalComparisonCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
354 {
355   char *pszExpression = NULL;
356   char *pszTmp = NULL;
357 
358   if (!psFilterNode || !FLTIsLogicalFilterType(psFilterNode->pszValue))
359     return NULL;
360 
361   /* -------------------------------------------------------------------- */
362   /*      OR and AND                                                      */
363   /* -------------------------------------------------------------------- */
364   if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
365     pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
366     if (!pszTmp)
367       return NULL;
368 
369     pszExpression = msStringConcatenate(pszExpression, "(");
370 
371     pszExpression = msStringConcatenate(pszExpression, pszTmp);
372     msFree(pszTmp);
373 
374     pszExpression = msStringConcatenate(pszExpression, " ");
375 
376     pszExpression = msStringConcatenate(pszExpression, psFilterNode->pszValue);
377 
378     pszExpression = msStringConcatenate(pszExpression, " ");
379 
380     pszTmp = FLTGetCommonExpression(psFilterNode->psRightNode, lp);
381     if (!pszTmp) {
382       msFree(pszExpression);
383       return NULL;
384     }
385 
386     pszExpression = msStringConcatenate(pszExpression, pszTmp);
387     msFree(pszTmp);
388 
389     pszExpression = msStringConcatenate(pszExpression, ")");
390   }
391   /* -------------------------------------------------------------------- */
392   /*      NOT                                                             */
393   /* -------------------------------------------------------------------- */
394   else if (psFilterNode->psLeftNode && strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
395     pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
396     if (!pszTmp)
397       return NULL;
398 
399     pszExpression = msStringConcatenate(pszExpression, "(NOT ");
400 
401     pszExpression = msStringConcatenate(pszExpression, pszTmp);
402     msFree(pszTmp);
403 
404     pszExpression = msStringConcatenate(pszExpression, ")");
405   }
406 
407   return pszExpression;
408 }
409 
FLTGetSpatialComparisonCommonExpression(FilterEncodingNode * psNode,layerObj * lp)410 char *FLTGetSpatialComparisonCommonExpression(FilterEncodingNode *psNode, layerObj *lp)
411 {
412   char *pszExpression = NULL;
413   shapeObj *psQueryShape = NULL;
414   double dfDistance = -1;
415   int nUnit = -1, nLayerUnit = -1;
416   char *pszWktText = NULL;
417   char szBuffer[256];
418   char *pszTmp=NULL;
419   projectionObj sProjTmp;
420   rectObj sQueryRect;
421   shapeObj *psTmpShape=NULL;
422   int bBBoxQuery = 0;
423   int bAlreadyReprojected = 0;
424 
425   if (psNode == NULL || lp == NULL)
426     return NULL;
427 
428   if (psNode->eType != FILTER_NODE_TYPE_SPATIAL)
429     return NULL;
430 
431   /* get the shape */
432   if (FLTIsBBoxFilter(psNode)) {
433     char szPolygon[512];
434     FLTGetBBOX(psNode, &sQueryRect);
435 
436     snprintf(szPolygon, sizeof(szPolygon),
437              "POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))",
438              sQueryRect.minx, sQueryRect.miny,
439              sQueryRect.minx, sQueryRect.maxy,
440              sQueryRect.maxx, sQueryRect.maxy,
441              sQueryRect.maxx, sQueryRect.miny,
442              sQueryRect.minx, sQueryRect.miny);
443 
444     psTmpShape = msShapeFromWKT(szPolygon);
445 
446     /*
447     ** This is a horrible hack to deal with world-extent requests and
448     ** reprojection. msProjectRect() detects if reprojection from longlat to
449     ** projected SRS, and in that case it transforms the bbox to -1e-15,-1e-15,1e15,1e15
450     ** to ensure that all features are returned.
451     **
452     ** Make wfs_200_cite_filter_bbox_world.xml and wfs_200_cite_postgis_bbox_world.xml pass
453     */
454     if (fabs(sQueryRect.minx - -180.0) < 1e-5 &&
455         fabs(sQueryRect.miny - -90.0) < 1e-5 &&
456         fabs(sQueryRect.maxx - 180.0) < 1e-5 &&
457         fabs(sQueryRect.maxy - 90.0) < 1e-5)
458     {
459       if (lp->projection.numargs > 0) {
460         if (psNode->pszSRS) {
461           msInitProjection(&sProjTmp);
462           msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
463         }
464         if (psNode->pszSRS) {
465           /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
466           if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
467             msProjectRect(&sProjTmp, &lp->projection, &sQueryRect);
468           }
469         } else if (lp->map->projection.numargs > 0)
470           msProjectRect(&lp->map->projection, &lp->projection, &sQueryRect);
471         if (psNode->pszSRS)
472           msFreeProjection(&sProjTmp);
473       }
474       if (sQueryRect.minx <= -1e14) {
475         msFreeShape(psTmpShape);
476         msFree(psTmpShape);
477         psTmpShape = (shapeObj*) msSmallMalloc(sizeof(shapeObj));
478         msInitShape(psTmpShape);
479         msRectToPolygon(sQueryRect, psTmpShape);
480         bAlreadyReprojected = 1;
481       }
482     }
483 
484     bBBoxQuery = 1;
485   } else {
486     /* other geos type operations */
487 
488     /* project shape to layer projection. If the proj is not part of the filter query,
489       assume that the cooredinates are in the map projection */
490 
491     psQueryShape = FLTGetShape(psNode, &dfDistance, &nUnit);
492 
493     if ((strcasecmp(psNode->pszValue, "DWithin") == 0 || strcasecmp(psNode->pszValue, "Beyond") == 0 ) && dfDistance > 0) {
494       nLayerUnit = lp->units;
495       if(nLayerUnit == -1) nLayerUnit = GetMapserverUnitUsingProj(&lp->projection);
496       if(nLayerUnit == -1) nLayerUnit = lp->map->units;
497       if(nLayerUnit == -1) nLayerUnit = GetMapserverUnitUsingProj(&lp->map->projection);
498 
499       if (nUnit >= 0 && nUnit != nLayerUnit)
500         dfDistance *= msInchesPerUnit(nUnit,0)/msInchesPerUnit(nLayerUnit,0); /* target is layer units */
501     }
502 
503     psTmpShape = psQueryShape;
504   }
505 
506   if (psTmpShape) {
507 
508     /*
509     ** target is layer projection
510     */
511     if (!bAlreadyReprojected && lp->projection.numargs > 0) {
512       if (psNode->pszSRS) {
513         msInitProjection(&sProjTmp);
514         msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
515       }
516       if (psNode->pszSRS) {
517         /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
518         if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
519           msProjectShape(&sProjTmp, &lp->projection, psTmpShape);
520         }
521       } else if (lp->map->projection.numargs > 0)
522         msProjectShape(&lp->map->projection, &lp->projection, psTmpShape);
523       if (psNode->pszSRS)
524         msFreeProjection(&sProjTmp);
525     }
526 
527     /* function name */
528     if (bBBoxQuery) {
529       sprintf(szBuffer, "%s", "intersects");
530     } else {
531       if (strncasecmp(psNode->pszValue, "intersect", 9) == 0)
532         sprintf(szBuffer, "%s", "intersects");
533       else {
534         pszTmp = msStrdup(psNode->pszValue);
535         msStringToLower(pszTmp);
536         sprintf(szBuffer, "%s", pszTmp);
537         msFree(pszTmp);
538       }
539     }
540     pszExpression = msStringConcatenate(pszExpression, szBuffer);
541     pszExpression = msStringConcatenate(pszExpression, "(");
542 
543     /* geometry binding */
544     sprintf(szBuffer, "%s", "[shape]");
545     pszExpression = msStringConcatenate(pszExpression, szBuffer);
546     pszExpression = msStringConcatenate(pszExpression, ",");
547 
548     /* filter geometry */
549     pszWktText = msGEOSShapeToWKT(psTmpShape);
550     sprintf(szBuffer, "%s", "fromText('");
551     pszExpression = msStringConcatenate(pszExpression, szBuffer);
552     pszExpression = msStringConcatenate(pszExpression, pszWktText);
553     sprintf(szBuffer, "%s", "')");
554     pszExpression = msStringConcatenate(pszExpression, szBuffer);
555     msGEOSFreeWKT(pszWktText);
556 
557     /* (optional) beyond/dwithin distance, always 0.0 since we apply the distance as a buffer earlier */
558     if ((strcasecmp(psNode->pszValue, "DWithin") == 0 || strcasecmp(psNode->pszValue, "Beyond") == 0)) {
559       // pszExpression = msStringConcatenate(pszExpression, ",0.0");
560       sprintf(szBuffer, ",%g", dfDistance);
561       pszExpression = msStringConcatenate(pszExpression, szBuffer);
562     }
563 
564     /* terminate the function */
565     pszExpression = msStringConcatenate(pszExpression, ") = TRUE");
566   }
567 
568   /*
569   ** Cleanup
570   */
571   if (bBBoxQuery) {
572     msFreeShape(psTmpShape);
573     msFree(psTmpShape);
574   }
575 
576   return pszExpression;
577 }
578 
FLTGetFeatureIdCommonExpression(FilterEncodingNode * psFilterNode,layerObj * lp)579 char *FLTGetFeatureIdCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
580 {
581   char *pszExpression = NULL;
582   int nTokens = 0, i=0, bString=0;
583   char **tokens = NULL;
584   const char *pszAttribute=NULL;
585 
586 #if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) || defined(USE_SOS_SVR)
587   if (psFilterNode->pszValue) {
588     pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
589     if (pszAttribute) {
590       tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
591       if (tokens && nTokens > 0) {
592         for (i=0; i<nTokens; i++) {
593           char *pszTmp = NULL;
594           int bufferSize = 0;
595           const char* pszId = tokens[i];
596           const char* pszDot = strrchr(pszId, '.');
597           if( pszDot )
598             pszId = pszDot + 1;
599 
600           if (i == 0) {
601             if(FLTIsNumeric(pszId) == MS_FALSE)
602               bString = 1;
603           }
604 
605           if (bString) {
606             bufferSize = 11+strlen(pszId)+strlen(pszAttribute)+1;
607             pszTmp = (char *)msSmallMalloc(bufferSize);
608             snprintf(pszTmp, bufferSize, "(\"[%s]\" ==\"%s\")" , pszAttribute, pszId);
609           } else {
610             bufferSize = 8+strlen(pszId)+strlen(pszAttribute)+1;
611             pszTmp = (char *)msSmallMalloc(bufferSize);
612             snprintf(pszTmp, bufferSize, "([%s] == %s)" , pszAttribute, pszId);
613           }
614 
615           if (pszExpression != NULL)
616             pszExpression = msStringConcatenate(pszExpression, " OR ");
617           else
618             pszExpression = msStringConcatenate(pszExpression, "(");
619           pszExpression = msStringConcatenate(pszExpression, pszTmp);
620           msFree(pszTmp);
621         }
622 
623         msFreeCharArray(tokens, nTokens);
624       }
625     }
626 
627     /* opening and closing brackets are needed for mapserver expressions */
628     if (pszExpression)
629       pszExpression = msStringConcatenate(pszExpression, ")");
630   }
631 #endif
632 
633   return pszExpression;
634 }
635 
FLTGetTimeExpression(FilterEncodingNode * psFilterNode,layerObj * lp)636 char* FLTGetTimeExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
637 {
638   char* pszExpression = NULL;
639   const char* pszTimeField;
640   const char* pszTimeValue;
641 
642   if (psFilterNode == NULL || lp == NULL)
643     return NULL;
644 
645   if (psFilterNode->eType != FILTER_NODE_TYPE_TEMPORAL)
646     return NULL;
647 
648   pszTimeValue = FLTGetDuring(psFilterNode, &pszTimeField);
649   if (pszTimeField && pszTimeValue) {
650     expressionObj old_filter;
651     msInitExpression(&old_filter);
652     msCopyExpression(&old_filter, &lp->filter); /* save existing filter */
653     msFreeExpression(&lp->filter);
654     if (msLayerSetTimeFilter(lp, pszTimeValue, pszTimeField) == MS_TRUE) {
655       pszExpression = msStrdup(lp->filter.string);
656     }
657     msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
658     msFreeExpression(&old_filter);
659   }
660   return pszExpression;
661 }
662 
FLTGetCommonExpression(FilterEncodingNode * psFilterNode,layerObj * lp)663 char *FLTGetCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
664 {
665   char *pszExpression = NULL;
666 
667   if (!psFilterNode)
668     return NULL;
669 
670   if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
671     if ( psFilterNode->psLeftNode && psFilterNode->psRightNode) {
672       if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
673         pszExpression = FLTGetBinaryComparisonCommonExpression(psFilterNode, lp);
674       else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
675         pszExpression = msStrdup(FLTGetIsLikeComparisonCommonExpression(psFilterNode).c_str());
676       else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0)
677         pszExpression = msStrdup(FLTGetIsBetweenComparisonCommonExpresssion(psFilterNode, lp).c_str());
678     }
679   } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
680     pszExpression = FLTGetLogicalComparisonCommonExpression(psFilterNode, lp);
681   } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
682     pszExpression = FLTGetSpatialComparisonCommonExpression(psFilterNode, lp);
683   } else if (psFilterNode->eType ==  FILTER_NODE_TYPE_FEATUREID) {
684     pszExpression = FLTGetFeatureIdCommonExpression(psFilterNode, lp);
685   } else if (psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL) {
686     pszExpression = FLTGetTimeExpression(psFilterNode, lp);
687   }
688 
689   return pszExpression;
690 }
691 
FLTApplyFilterToLayerCommonExpression(mapObj * map,int iLayerIndex,const char * pszExpression)692 int FLTApplyFilterToLayerCommonExpression(mapObj *map, int iLayerIndex, const char *pszExpression)
693 {
694   return FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex, pszExpression, map->extent);
695 }
696 
697 /* rect must be in map->projection */
FLTApplyFilterToLayerCommonExpressionWithRect(mapObj * map,int iLayerIndex,const char * pszExpression,rectObj rect)698 int FLTApplyFilterToLayerCommonExpressionWithRect(mapObj *map, int iLayerIndex, const char *pszExpression, rectObj rect)
699 {
700   int retval;
701   int save_startindex;
702   int save_maxfeatures;
703   int save_only_cache_result_count;
704   int save_cache_shapes;
705   int save_max_cached_shape_count;
706   int save_max_cached_shape_ram_amount;
707 
708   save_startindex = map->query.startindex;
709   save_maxfeatures = map->query.maxfeatures;
710   save_only_cache_result_count = map->query.only_cache_result_count;
711   save_cache_shapes = map->query.cache_shapes;
712   save_max_cached_shape_count = map->query.max_cached_shape_count;
713   save_max_cached_shape_ram_amount = map->query.max_cached_shape_ram_amount;
714   msInitQuery(&(map->query));
715   map->query.startindex = save_startindex;
716   map->query.maxfeatures = save_maxfeatures;
717   map->query.only_cache_result_count = save_only_cache_result_count;
718   map->query.cache_shapes = save_cache_shapes;
719   map->query.max_cached_shape_count = save_max_cached_shape_count;
720   map->query.max_cached_shape_ram_amount = save_max_cached_shape_ram_amount;
721 
722   map->query.mode = MS_QUERY_MULTIPLE;
723   map->query.layer = iLayerIndex;
724 
725   map->query.rect = rect;
726 
727   if( pszExpression )
728   {
729     map->query.type = MS_QUERY_BY_FILTER;
730     msInitExpression(&map->query.filter);
731     map->query.filter.string = msStrdup(pszExpression);
732     map->query.filter.type = MS_EXPRESSION; /* a logical expression */
733 
734     retval = msQueryByFilter(map);
735   }
736   else
737   {
738     map->query.type = MS_QUERY_BY_RECT;
739     retval = msQueryByRect(map);
740   }
741 
742   return retval;
743 }
744