LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbUtils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 166 203 81.8 %
Date: 2024-05-06 22:33:47 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Different utility functions used in FileGDB OGR driver.
       5             :  * Author:   Ragi Yaser Burhum, ragi@burhum.com
       6             :  *           Paul Ramsey, pramsey at cleverelephant.ca
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2010, Ragi Yaser Burhum
      10             :  * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
      11             :  * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * Permission is hereby granted, free of charge, to any person obtaining a
      14             :  * copy of this software and associated documentation files (the "Software"),
      15             :  * to deal in the Software without restriction, including without limitation
      16             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17             :  * and/or sell copies of the Software, and to permit persons to whom the
      18             :  * Software is furnished to do so, subject to the following conditions:
      19             :  *
      20             :  * The above copyright notice and this permission notice shall be included
      21             :  * in all copies or substantial portions of the Software.
      22             :  *
      23             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29             :  * DEALINGS IN THE SOFTWARE.
      30             :  ****************************************************************************/
      31             : 
      32             : #include "FGdbUtils.h"
      33             : #include <algorithm>
      34             : 
      35             : #include "ogr_api.h"
      36             : #include "ogrpgeogeometry.h"
      37             : 
      38             : using std::string;
      39             : 
      40             : /*************************************************************************/
      41             : /*                          StringToWString()                            */
      42             : /*************************************************************************/
      43             : 
      44       20779 : std::wstring StringToWString(const std::string &utf8string)
      45             : {
      46             :     wchar_t *pszUTF16 =
      47       20779 :         CPLRecodeToWChar(utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
      48       20779 :     std::wstring utf16string = pszUTF16;
      49       20779 :     CPLFree(pszUTF16);
      50       20779 :     return utf16string;
      51             : }
      52             : 
      53             : /*************************************************************************/
      54             : /*                          WStringToString()                            */
      55             : /*************************************************************************/
      56             : 
      57       10428 : std::string WStringToString(const std::wstring &utf16string)
      58             : {
      59             :     char *pszUTF8 =
      60       10428 :         CPLRecodeFromWChar(utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
      61       10428 :     std::string utf8string = pszUTF8;
      62       10428 :     CPLFree(pszUTF8);
      63       10428 :     return utf8string;
      64             : }
      65             : 
      66             : /*************************************************************************/
      67             : /*                                GDBErr()                               */
      68             : /*************************************************************************/
      69             : 
      70          26 : bool GDBErr(long int hr, const std::string &desc, CPLErr errType,
      71             :             const char *pszAddMsg)
      72             : {
      73          26 :     std::wstring fgdb_error_desc_w;
      74             :     fgdbError er;
      75          26 :     er = FileGDBAPI::ErrorInfo::GetErrorDescription(static_cast<fgdbError>(hr),
      76             :                                                     fgdb_error_desc_w);
      77          26 :     if (er == S_OK)
      78             :     {
      79          52 :         std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
      80          26 :         CPLError(errType, CPLE_AppDefined, "%s (%s)%s", desc.c_str(),
      81             :                  fgdb_error_desc.c_str(), pszAddMsg);
      82             :     }
      83             :     else
      84             :     {
      85           0 :         CPLError(errType, CPLE_AppDefined, "Error (%ld): %s%s", hr,
      86             :                  desc.c_str(), pszAddMsg);
      87             :     }
      88             :     // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it,
      89             :     // it causes crashes in case of repeated errors
      90             :     // FileGDBAPI::ErrorInfo::ClearErrors();
      91             : 
      92          52 :     return false;
      93             : }
      94             : 
      95             : /*************************************************************************/
      96             : /*                            GDBDebug()                                 */
      97             : /*************************************************************************/
      98             : 
      99           0 : bool GDBDebug(long int hr, const std::string &desc)
     100             : {
     101           0 :     std::wstring fgdb_error_desc_w;
     102             :     fgdbError er;
     103           0 :     er = FileGDBAPI::ErrorInfo::GetErrorDescription(static_cast<fgdbError>(hr),
     104             :                                                     fgdb_error_desc_w);
     105           0 :     if (er == S_OK)
     106             :     {
     107           0 :         std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
     108           0 :         CPLDebug("FGDB", "%s (%s)", desc.c_str(), fgdb_error_desc.c_str());
     109             :     }
     110             :     else
     111             :     {
     112           0 :         CPLDebug("FGDB", "%s", desc.c_str());
     113             :     }
     114             :     // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it,
     115             :     // it causes crashes in case of repeated errors
     116             :     // FileGDBAPI::ErrorInfo::ClearErrors();
     117             : 
     118           0 :     return false;
     119             : }
     120             : 
     121             : /*************************************************************************/
     122             : /*                            GDBToOGRGeometry()                         */
     123             : /*************************************************************************/
     124             : 
     125         370 : bool GDBToOGRGeometry(const std::string &geoType, bool hasZ, bool hasM,
     126             :                       OGRwkbGeometryType *pOut)
     127             : {
     128         370 :     if (geoType == "esriGeometryPoint")
     129             :     {
     130          88 :         *pOut = wkbPoint;
     131             :     }
     132         282 :     else if (geoType == "esriGeometryMultipoint")
     133             :     {
     134          35 :         *pOut = wkbMultiPoint;
     135             :     }
     136         247 :     else if (geoType == "esriGeometryLine")
     137             :     {
     138           0 :         *pOut = wkbLineString;
     139             :     }
     140         247 :     else if (geoType == "esriGeometryPolyline")
     141             :     {
     142          80 :         *pOut = wkbMultiLineString;
     143             :     }
     144         201 :     else if (geoType == "esriGeometryPolygon" ||
     145          34 :              geoType == "esriGeometryMultiPatch")
     146             :     {
     147         167 :         *pOut = wkbMultiPolygon;  // no mapping to single polygon
     148             :     }
     149             :     else
     150             :     {
     151           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     152             :                  "Cannot map esriGeometryType(%s) to OGRwkbGeometryType",
     153             :                  geoType.c_str());
     154           0 :         return false;
     155             :     }
     156         370 :     if (hasZ)
     157         145 :         *pOut = wkbSetZ(*pOut);
     158         370 :     if (hasM)
     159           7 :         *pOut = wkbSetM(*pOut);
     160             : 
     161         370 :     return true;
     162             : }
     163             : 
     164             : /*************************************************************************/
     165             : /*                            OGRGeometryToGDB()                         */
     166             : /*************************************************************************/
     167             : 
     168         137 : bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType,
     169             :                       bool *hasZ, bool *hasM)
     170             : {
     171         137 :     *hasZ = wkbHasZ(ogrType);
     172         137 :     *hasM = wkbHasM(ogrType);
     173         137 :     switch (wkbFlatten(ogrType))
     174             :     {
     175          42 :         case wkbPoint:
     176             :         {
     177          42 :             *gdbType = "esriGeometryPoint";
     178          42 :             break;
     179             :         }
     180             : 
     181          13 :         case wkbMultiPoint:
     182             :         {
     183          13 :             *gdbType = "esriGeometryMultipoint";
     184          13 :             break;
     185             :         }
     186             : 
     187          27 :         case wkbLineString:
     188             :         case wkbMultiLineString:
     189             :         {
     190          27 :             *gdbType = "esriGeometryPolyline";
     191          27 :             break;
     192             :         }
     193             : 
     194          49 :         case wkbPolygon:
     195             :         case wkbMultiPolygon:
     196             :         {
     197          49 :             *gdbType = "esriGeometryPolygon";
     198          49 :             break;
     199             :         }
     200             : 
     201           6 :         case wkbTIN:
     202             :         case wkbPolyhedralSurface:
     203             :         {
     204           6 :             *gdbType = "esriGeometryMultiPatch";
     205           6 :             break;
     206             :         }
     207             : 
     208           0 :         default:
     209             :         {
     210           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     211             :                      "Cannot map OGRwkbGeometryType (%s) to ESRI type",
     212             :                      OGRGeometryTypeToName(ogrType));
     213           0 :             return false;
     214             :         }
     215             :     }
     216         137 :     return true;
     217             : }
     218             : 
     219             : /*************************************************************************/
     220             : /*                            OGRToGDBFieldType()                        */
     221             : /*************************************************************************/
     222             : 
     223        1375 : bool OGRToGDBFieldType(OGRFieldType ogrType, OGRFieldSubType eSubType,
     224             :                        std::string *gdbType)
     225             : {
     226        1375 :     switch (ogrType)
     227             :     {
     228         426 :         case OFTInteger:
     229             :         {
     230         426 :             if (eSubType == OFSTInt16)
     231         104 :                 *gdbType = "esriFieldTypeSmallInteger";
     232             :             else
     233         322 :                 *gdbType = "esriFieldTypeInteger";
     234         426 :             break;
     235             :         }
     236         321 :         case OFTReal:
     237             :         case OFTInteger64:
     238             :         {
     239         321 :             if (eSubType == OFSTFloat32)
     240         102 :                 *gdbType = "esriFieldTypeSingle";
     241             :             else
     242         219 :                 *gdbType = "esriFieldTypeDouble";
     243         321 :             break;
     244             :         }
     245         320 :         case OFTString:
     246             :         {
     247         320 :             *gdbType = "esriFieldTypeString";
     248         320 :             break;
     249             :         }
     250         204 :         case OFTBinary:
     251             :         {
     252         204 :             *gdbType = "esriFieldTypeBlob";
     253         204 :             break;
     254             :         }
     255         104 :         case OFTDate:
     256             :         case OFTDateTime:
     257             :         {
     258         104 :             *gdbType = "esriFieldTypeDate";
     259         104 :             break;
     260             :         }
     261           0 :         default:
     262             :         {
     263           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     264             :                      "Cannot map OGR field type (%s)",
     265             :                      OGR_GetFieldTypeName(ogrType));
     266           0 :             return false;
     267             :         }
     268             :     }
     269             : 
     270        1375 :     return true;
     271             : }
     272             : 
     273             : /*************************************************************************/
     274             : /*                       GDBFieldTypeToLengthInBytes()                   */
     275             : /*************************************************************************/
     276             : 
     277        1375 : bool GDBFieldTypeToLengthInBytes(const std::string &gdbType, int &lengthOut)
     278             : {
     279             :     /* Length based on
     280             :      * FileGDB_API/samples/XMLsamples/OneOfEachFieldType.xml */
     281             :     /* Length is in bytes per doc of FileGDB_API/xmlResources/FileGDBAPI.xsd */
     282        1375 :     if (gdbType == "esriFieldTypeSmallInteger")
     283             :     {
     284         206 :         lengthOut = 2;
     285             :     }
     286        1169 :     else if (gdbType == "esriFieldTypeInteger")
     287             :     {
     288         220 :         lengthOut = 4;
     289             :     }
     290         949 :     else if (gdbType == "esriFieldTypeSingle")
     291             :     {
     292         204 :         lengthOut = 4;
     293             :     }
     294         745 :     else if (gdbType == "esriFieldTypeDouble")
     295             :     {
     296         117 :         lengthOut = 8;
     297             :     }
     298         628 :     else if (gdbType == "esriFieldTypeString" || gdbType == "esriFieldTypeXML")
     299             :     {
     300         217 :         lengthOut = atoi(CPLGetConfigOption("FGDB_STRING_WIDTH", "65536"));
     301             :     }
     302         411 :     else if (gdbType == "esriFieldTypeDate")
     303             :     {
     304         104 :         lengthOut = 8;
     305             :     }
     306         307 :     else if (gdbType == "esriFieldTypeOID")
     307             :     {
     308           0 :         lengthOut = 4;
     309             :     }
     310         307 :     else if (gdbType == "esriFieldTypeGUID")
     311             :     {
     312         102 :         lengthOut = 16;
     313             :     }
     314         205 :     else if (gdbType == "esriFieldTypeBlob")
     315             :     {
     316         204 :         lengthOut = 0;
     317             :     }
     318           1 :     else if (gdbType == "esriFieldTypeGlobalID")
     319             :     {
     320           1 :         lengthOut = 38;
     321             :     }
     322             :     else
     323             :     {
     324           0 :         CPLError(CE_Warning, CPLE_AppDefined, "Cannot map ESRI field type (%s)",
     325             :                  gdbType.c_str());
     326           0 :         return false;
     327             :     }
     328             : 
     329        1375 :     return true;
     330             : }
     331             : 
     332             : /*************************************************************************/
     333             : /*                        GDBGeometryToOGRGeometry()                     */
     334             : /*************************************************************************/
     335             : 
     336        1832 : bool GDBGeometryToOGRGeometry(bool forceMulti,
     337             :                               FileGDBAPI::ShapeBuffer *pGdbGeometry,
     338             :                               OGRSpatialReference *pOGRSR,
     339             :                               OGRGeometry **ppOutGeometry)
     340             : {
     341             : 
     342        1832 :     OGRGeometry *pOGRGeometry = nullptr;
     343             : 
     344             :     OGRErr eErr =
     345        3664 :         OGRCreateFromShapeBin(pGdbGeometry->shapeBuffer, &pOGRGeometry,
     346        1832 :                               static_cast<int>(pGdbGeometry->inUseLength));
     347             : 
     348             :     // OGRErr eErr =
     349             :     // OGRGeometryFactory::createFromWkb(pGdbGeometry->shapeBuffer, pOGRSR,
     350             :     // &pOGRGeometry, pGdbGeometry->inUseLength );
     351             : 
     352        1832 :     if (eErr != OGRERR_NONE)
     353             :     {
     354           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     355             :                  "Failed attempting to import GDB WKB Geometry. "
     356             :                  "OGRGeometryFactory err:%d",
     357             :                  eErr);
     358           0 :         return false;
     359             :     }
     360             : 
     361        1832 :     if (pOGRGeometry != nullptr)
     362             :     {
     363             :         // force geometries to multi if requested
     364             : 
     365             :         // If it is a polygon, force to MultiPolygon since we always produce
     366             :         // multipolygons
     367             :         OGRwkbGeometryType eFlattenType =
     368        1832 :             wkbFlatten(pOGRGeometry->getGeometryType());
     369        1832 :         if (eFlattenType == wkbPolygon)
     370             :         {
     371         561 :             pOGRGeometry =
     372         561 :                 OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry);
     373             :         }
     374        1271 :         else if (eFlattenType == wkbCurvePolygon)
     375             :         {
     376          12 :             OGRMultiSurface *poMS = new OGRMultiSurface();
     377          12 :             poMS->addGeometryDirectly(pOGRGeometry);
     378          12 :             pOGRGeometry = poMS;
     379             :         }
     380        1259 :         else if (forceMulti)
     381             :         {
     382         688 :             if (eFlattenType == wkbLineString)
     383             :             {
     384         342 :                 pOGRGeometry =
     385         342 :                     OGRGeometryFactory::forceToMultiLineString(pOGRGeometry);
     386             :             }
     387         346 :             else if (eFlattenType == wkbCompoundCurve)
     388             :             {
     389           9 :                 OGRMultiCurve *poMC = new OGRMultiCurve();
     390           9 :                 poMC->addGeometryDirectly(pOGRGeometry);
     391           9 :                 pOGRGeometry = poMC;
     392             :             }
     393         337 :             else if (eFlattenType == wkbPoint)
     394             :             {
     395           0 :                 pOGRGeometry =
     396           0 :                     OGRGeometryFactory::forceToMultiPoint(pOGRGeometry);
     397             :             }
     398             :         }
     399             : 
     400        1832 :         if (pOGRGeometry)
     401        1832 :             pOGRGeometry->assignSpatialReference(pOGRSR);
     402             :     }
     403             : 
     404        1832 :     *ppOutGeometry = pOGRGeometry;
     405             : 
     406        1832 :     return true;
     407             : }
     408             : 
     409             : /*************************************************************************/
     410             : /*                         GDBToOGRSpatialReference()                    */
     411             : /*************************************************************************/
     412             : 
     413           7 : bool GDBToOGRSpatialReference(const string &wkt, OGRSpatialReference **ppSR)
     414             : {
     415           7 :     if (wkt.empty())
     416             :     {
     417           0 :         CPLError(CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL");
     418           0 :         return false;
     419             :     }
     420             : 
     421           7 :     *ppSR = new OGRSpatialReference(wkt.c_str());
     422           7 :     (*ppSR)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     423             : 
     424           7 :     OGRErr result = (*ppSR)->morphFromESRI();
     425             : 
     426           7 :     if (result == OGRERR_NONE)
     427             :     {
     428           7 :         if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
     429             :         {
     430           7 :             int nEntries = 0;
     431           7 :             int *panConfidence = nullptr;
     432             :             OGRSpatialReferenceH *pahSRS =
     433           7 :                 (*ppSR)->FindMatches(nullptr, &nEntries, &panConfidence);
     434           7 :             if (nEntries == 1 && panConfidence[0] == 100)
     435             :             {
     436           0 :                 (*ppSR)->Release();
     437           0 :                 (*ppSR) = reinterpret_cast<OGRSpatialReference *>(pahSRS[0]);
     438           0 :                 (*ppSR)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     439           0 :                 CPLFree(pahSRS);
     440             :             }
     441             :             else
     442             :             {
     443           7 :                 OSRFreeSRSArray(pahSRS);
     444             :             }
     445           7 :             CPLFree(panConfidence);
     446             :         }
     447             :         else
     448             :         {
     449           0 :             (*ppSR)->AutoIdentifyEPSG();
     450             :         }
     451             : 
     452           7 :         return true;
     453             :     }
     454             :     else
     455             :     {
     456           0 :         delete *ppSR;
     457           0 :         *ppSR = nullptr;
     458             : 
     459           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     460             :                  "Failed morphing from ESRI Geometry: %s", wkt.c_str());
     461             : 
     462           0 :         return false;
     463             :     }
     464             : }
     465             : 
     466             : /*************************************************************************/
     467             : /*                           FGDB_CPLAddXMLAttribute()                   */
     468             : /*************************************************************************/
     469             : 
     470             : /* Utility method for attributing nodes */
     471        7696 : void FGDB_CPLAddXMLAttribute(CPLXMLNode *node, const char *attrname,
     472             :                              const char *attrvalue)
     473             : {
     474        7696 :     if (!node)
     475           0 :         return;
     476        7696 :     CPLCreateXMLNode(CPLCreateXMLNode(node, CXT_Attribute, attrname), CXT_Text,
     477             :                      attrvalue);
     478             : }
     479             : 
     480             : /*************************************************************************/
     481             : /*                          FGDBLaunderName()                            */
     482             : /*************************************************************************/
     483             : 
     484        1523 : std::wstring FGDBLaunderName(const std::wstring &name)
     485             : {
     486        1523 :     std::wstring newName = name;
     487             : 
     488             :     // https://support.esri.com/en/technical-article/000005588
     489             : 
     490             :     // "Do not start field or table names with an underscore or a number."
     491             :     // But we can see in the wild table names starting with underscore...
     492             :     // (cf https://github.com/OSGeo/gdal/issues/4112)
     493        1523 :     if (!newName.empty() && newName[0] >= '0' && newName[0] <= '9')
     494             :     {
     495           2 :         newName = StringToWString("_") + newName;
     496             :     }
     497             : 
     498             :     // "Essentially, eliminate anything that is not alphanumeric or an
     499             :     // underscore." Note: alphanumeric unicode is supported
     500       10725 :     for (size_t i = 0; i < newName.size(); i++)
     501             :     {
     502       17307 :         if (!(newName[i] == '_' || (newName[i] >= '0' && newName[i] <= '9') ||
     503        8105 :               (newName[i] >= 'a' && newName[i] <= 'z') ||
     504         312 :               (newName[i] >= 'A' && newName[i] <= 'Z') || newName[i] >= 128))
     505             :         {
     506          20 :             newName[i] = '_';
     507             :         }
     508             :     }
     509             : 
     510        1523 :     return newName;
     511             : }
     512             : 
     513             : /*************************************************************************/
     514             : /*                     FGDBEscapeUnsupportedPrefixes()                   */
     515             : /*************************************************************************/
     516             : 
     517         148 : std::wstring FGDBEscapeUnsupportedPrefixes(const std::wstring &className)
     518             : {
     519         148 :     std::wstring newName = className;
     520             :     // From ESRI docs
     521             :     // Feature classes starting with these strings are unsupported.
     522             :     static const char *const UNSUPPORTED_PREFIXES[] = {"sde_", "gdb_", "delta_",
     523             :                                                        nullptr};
     524             : 
     525         589 :     for (int i = 0; UNSUPPORTED_PREFIXES[i] != nullptr; i++)
     526             :     {
     527             :         // cppcheck-suppress stlIfStrFind
     528         442 :         if (newName.find(StringToWString(UNSUPPORTED_PREFIXES[i])) == 0)
     529             :         {
     530             :             // Normally table names shouldn't start with underscore, but
     531             :             // there are such in the wild (cf
     532             :             // https://github.com/OSGeo/gdal/issues/4112)
     533           1 :             newName = StringToWString("_") + newName;
     534           1 :             break;
     535             :         }
     536             :     }
     537             : 
     538         148 :     return newName;
     539             : }
     540             : 
     541             : /*************************************************************************/
     542             : /*                        FGDBEscapeReservedKeywords()                   */
     543             : /*************************************************************************/
     544             : 
     545        1523 : std::wstring FGDBEscapeReservedKeywords(const std::wstring &name)
     546             : {
     547        3046 :     std::string newName = WStringToString(name);
     548        3046 :     std::string upperName = CPLString(newName).toupper();
     549             : 
     550             :     // From ESRI docs
     551             :     static const char *const RESERVED_WORDS[] = {
     552             :         FGDB_OID_NAME, "ADD",   "ALTER",  "AND",    "AS",    "ASC",
     553             :         "BETWEEN",     "BY",    "COLUMN", "CREATE", "DATE",  "DELETE",
     554             :         "DESC",        "DROP",  "EXISTS", "FOR",    "FROM",  "IN",
     555             :         "INSERT",      "INTO",  "IS",     "LIKE",   "NOT",   "NULL",
     556             :         "OR",          "ORDER", "SELECT", "SET",    "TABLE", "UPDATE",
     557             :         "VALUES",      "WHERE", nullptr};
     558             : 
     559             :     // Append an underscore to any FGDB reserved words used as field names
     560             :     // This is the same behavior ArcCatalog follows.
     561       50227 :     for (int i = 0; RESERVED_WORDS[i] != nullptr; i++)
     562             :     {
     563       48706 :         const char *w = RESERVED_WORDS[i];
     564       48706 :         if (upperName == w)
     565             :         {
     566           2 :             newName += '_';
     567           2 :             break;
     568             :         }
     569             :     }
     570             : 
     571        3046 :     return StringToWString(newName);
     572             : }

Generated by: LCOV version 1.14