LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/hana - ogrhanafeaturereader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 161 291 55.3 %
Date: 2024-05-13 13:33:37 Functions: 23 34 67.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SAP HANA Spatial Driver
       4             :  * Purpose:  OGRHanaFeatureReader class implementation
       5             :  * Author:   Maxim Rylov
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, SAP SE
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "ogrhanafeaturereader.h"
      30             : #include "ogrhanautils.h"
      31             : 
      32             : #include "cpl_time.h"
      33             : 
      34             : #include <algorithm>
      35             : #include <cstring>
      36             : #include <ctime>
      37             : #include <limits>
      38             : #include <sstream>
      39             : 
      40             : #include "odbc/Types.h"
      41             : 
      42             : namespace OGRHANA
      43             : {
      44             : namespace
      45             : {
      46             : 
      47             : template <typename T>
      48           0 : odbc::String CreateStringFromValues(const T *elements, int numElements,
      49             :                                     std::string (*toString)(T e))
      50             : {
      51           0 :     if (numElements == 0)
      52           0 :         return odbc::String();
      53             : 
      54           0 :     std::ostringstream os;
      55           0 :     for (int i = 0; i < numElements; ++i)
      56             :     {
      57           0 :         if (i > 0)
      58           0 :             os << ARRAY_VALUES_DELIMITER;
      59           0 :         os << toString(elements[i]);
      60             :     }
      61           0 :     return odbc::String(os.str());
      62             : }
      63             : 
      64           1 : template <typename T> T castInt(int value)
      65             : {
      66           2 :     if (value < std::numeric_limits<T>::min() ||
      67           1 :         value > std::numeric_limits<T>::max())
      68           0 :         throw std::overflow_error("Integer value lies outside of the range");
      69           1 :     return static_cast<T>(value);
      70             : }
      71             : 
      72             : // Specialization to make Coverity Scan happy
      73           1 : template <> int castInt(int value)
      74             : {
      75           1 :     return value;
      76             : }
      77             : 
      78           2 : template <typename T> T strToInt(const char *value)
      79             : {
      80           2 :     return castInt<T>(std::stoi(value));
      81             : }
      82             : 
      83             : }  // anonymous namespace
      84             : 
      85         133 : OGRHanaFeatureReader::OGRHanaFeatureReader(const OGRFeature &feature)
      86         133 :     : feature_(feature)
      87             : {
      88         133 : }
      89             : 
      90           1 : odbc::Boolean OGRHanaFeatureReader::GetFieldAsBoolean(int fieldIndex) const
      91             : {
      92           1 :     if (IsFieldSet(fieldIndex))
      93           0 :         return feature_.GetFieldAsInteger(fieldIndex) == 1;
      94             : 
      95           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
      96           1 :     if (defaultValue == nullptr)
      97           0 :         return odbc::Boolean();
      98             : 
      99           1 :     return (EQUAL(defaultValue, "1") || EQUAL(defaultValue, "'t'"));
     100             : }
     101             : 
     102           0 : odbc::Byte OGRHanaFeatureReader::GetFieldAsByte(int fieldIndex) const
     103             : {
     104           0 :     if (IsFieldSet(fieldIndex))
     105             :         return odbc::Byte(
     106           0 :             castInt<std::int8_t>(feature_.GetFieldAsInteger(fieldIndex)));
     107             : 
     108           0 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     109           0 :     if (defaultValue == nullptr)
     110           0 :         return odbc::Byte();
     111           0 :     return odbc::Byte(strToInt<std::int8_t>(defaultValue));
     112             : }
     113             : 
     114           1 : odbc::Short OGRHanaFeatureReader::GetFieldAsShort(int fieldIndex) const
     115             : {
     116           1 :     if (IsFieldSet(fieldIndex))
     117             :         return odbc::Short(
     118           0 :             castInt<std::int16_t>(feature_.GetFieldAsInteger(fieldIndex)));
     119             : 
     120           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     121           1 :     if (defaultValue == nullptr)
     122           0 :         return odbc::Short();
     123           1 :     return odbc::Short(strToInt<std::int16_t>(defaultValue));
     124             : }
     125             : 
     126           2 : odbc::Int OGRHanaFeatureReader::GetFieldAsInt(int fieldIndex) const
     127             : {
     128           2 :     if (IsFieldSet(fieldIndex))
     129           0 :         return odbc::Int(feature_.GetFieldAsInteger(fieldIndex));
     130             : 
     131           2 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     132           2 :     if (defaultValue == nullptr)
     133           1 :         return odbc::Int();
     134           1 :     return odbc::Int(strToInt<int>(defaultValue));
     135             : }
     136             : 
     137         125 : odbc::Long OGRHanaFeatureReader::GetFieldAsLong(int fieldIndex) const
     138             : {
     139         125 :     if (IsFieldSet(fieldIndex))
     140         109 :         return odbc::Long(feature_.GetFieldAsInteger64(fieldIndex));
     141             : 
     142          16 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     143          16 :     if (defaultValue == nullptr)
     144          15 :         return odbc::Long();
     145           1 :     return odbc::Long(std::stol(defaultValue));
     146             : }
     147             : 
     148           1 : odbc::Float OGRHanaFeatureReader::GetFieldAsFloat(int fieldIndex) const
     149             : {
     150           1 :     if (IsFieldSet(fieldIndex))
     151             :     {
     152           0 :         double dValue = feature_.GetFieldAsDouble(fieldIndex);
     153           0 :         return odbc::Float(static_cast<float>(dValue));
     154             :     }
     155             : 
     156           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     157           1 :     if (defaultValue == nullptr)
     158           0 :         return odbc::Float();
     159           1 :     return odbc::Float(std::stof(defaultValue));
     160             : }
     161             : 
     162         126 : odbc::Double OGRHanaFeatureReader::GetFieldAsDouble(int fieldIndex) const
     163             : {
     164         126 :     if (IsFieldSet(fieldIndex))
     165         108 :         return odbc::Double(feature_.GetFieldAsDouble(fieldIndex));
     166          18 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     167          18 :     if (defaultValue == nullptr)
     168          16 :         return odbc::Double();
     169           2 :     return odbc::Double(std::stod(defaultValue));
     170             : }
     171             : 
     172           0 : odbc::String OGRHanaFeatureReader::GetFieldAsString(int fieldIndex,
     173             :                                                     int maxCharLength) const
     174             : {
     175           0 :     auto getString = [&](const char *str)
     176             :     {
     177           0 :         if (str == nullptr)
     178           0 :             return odbc::String();
     179             : 
     180           0 :         if (maxCharLength > 0 &&
     181           0 :             std::strlen(str) > static_cast<std::size_t>(maxCharLength))
     182             :             return odbc::String(
     183           0 :                 std::string(str, static_cast<std::size_t>(maxCharLength)));
     184           0 :         return odbc::String(str);
     185           0 :     };
     186             : 
     187           0 :     if (IsFieldSet(fieldIndex))
     188           0 :         return getString(feature_.GetFieldAsString(fieldIndex));
     189             : 
     190           0 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     191           0 :     if (defaultValue == nullptr)
     192           0 :         return odbc::String();
     193             : 
     194           0 :     if (defaultValue[0] == '\'' &&
     195           0 :         defaultValue[strlen(defaultValue) - 1] == '\'')
     196             :     {
     197           0 :         CPLString str(defaultValue + 1);
     198           0 :         str.resize(str.size() - 1);
     199           0 :         char *tmp = CPLUnescapeString(str, nullptr, CPLES_SQL);
     200           0 :         odbc::String ret = getString(tmp);
     201           0 :         CPLFree(tmp);
     202           0 :         return ret;
     203             :     }
     204             : 
     205           0 :     return odbc::String(defaultValue);
     206             : }
     207             : 
     208         250 : odbc::String OGRHanaFeatureReader::GetFieldAsNString(int fieldIndex,
     209             :                                                      int maxCharLength) const
     210             : {
     211         125 :     auto getString = [&](const char *str)
     212             :     {
     213         125 :         if (str == nullptr)
     214           0 :             return odbc::String();
     215             : 
     216         125 :         if (maxCharLength <= 0)
     217           0 :             return odbc::String(std::string(str));
     218             : 
     219         125 :         int nSrcLen = static_cast<int>(std::strlen(str));
     220         125 :         int nSrcLenUTF = CPLStrlenUTF8(str);
     221             : 
     222         125 :         if (nSrcLenUTF > maxCharLength)
     223             :         {
     224           1 :             CPLDebug("HANA",
     225             :                      "Truncated field value '%s' at index %d to %d characters.",
     226           1 :                      str, fieldIndex, maxCharLength);
     227             : 
     228           1 :             int iUTF8Char = 0;
     229           9 :             for (int iChar = 0; iChar < nSrcLen; ++iChar)
     230             :             {
     231           9 :                 if ((str[iChar] & 0xc0) != 0x80)
     232             :                 {
     233           9 :                     if (iUTF8Char == maxCharLength)
     234             :                     {
     235           1 :                         nSrcLen = iChar;
     236           1 :                         break;
     237             :                     }
     238           8 :                     ++iUTF8Char;
     239             :                 }
     240             :             }
     241             :         }
     242             : 
     243             :         return odbc::String(
     244         125 :             std::string(str, static_cast<std::size_t>(nSrcLen)));
     245         250 :     };
     246             : 
     247         250 :     if (IsFieldSet(fieldIndex))
     248         124 :         return getString(feature_.GetFieldAsString(fieldIndex));
     249             : 
     250         126 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     251         126 :     if (defaultValue == nullptr)
     252         125 :         return odbc::String();
     253             : 
     254           1 :     if (defaultValue[0] == '\'' &&
     255           1 :         defaultValue[strlen(defaultValue) - 1] == '\'')
     256             :     {
     257           2 :         CPLString str(defaultValue + 1);
     258           1 :         str.resize(str.size() - 1);
     259           1 :         char *tmp = CPLUnescapeString(str, nullptr, CPLES_SQL);
     260           2 :         odbc::String ret = getString(tmp);
     261           1 :         CPLFree(tmp);
     262           1 :         return ret;
     263             :     }
     264             : 
     265           0 :     return odbc::String(defaultValue);
     266             : }
     267             : 
     268           1 : odbc::Date OGRHanaFeatureReader::GetFieldAsDate(int fieldIndex) const
     269             : {
     270           1 :     if (IsFieldSet(fieldIndex))
     271             :     {
     272           0 :         int year = 0;
     273           0 :         int month = 0;
     274           0 :         int day = 0;
     275           0 :         int hour = 0;
     276           0 :         int minute = 0;
     277           0 :         int timeZoneFlag = 0;
     278           0 :         float second = 0.0f;
     279           0 :         feature_.GetFieldAsDateTime(fieldIndex, &year, &month, &day, &hour,
     280             :                                     &minute, &second, &timeZoneFlag);
     281             : 
     282           0 :         return odbc::makeNullable<odbc::date>(year, month, day);
     283             :     }
     284             : 
     285           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     286           1 :     if (defaultValue == nullptr)
     287           0 :         return odbc::Date();
     288             : 
     289           1 :     if (EQUAL(defaultValue, "CURRENT_DATE"))
     290             :     {
     291           0 :         std::time_t t = std::time(nullptr);
     292           0 :         tm *now = std::localtime(&t);
     293           0 :         if (now == nullptr)
     294           0 :             return odbc::Date();
     295           0 :         return odbc::makeNullable<odbc::date>(now->tm_year + 1900,
     296           0 :                                               now->tm_mon + 1, now->tm_mday);
     297             :     }
     298             : 
     299             :     int year, month, day;
     300           1 :     sscanf(defaultValue, "'%04d/%02d/%02d'", &year, &month, &day);
     301             : 
     302           1 :     return odbc::makeNullable<odbc::date>(year, month, day);
     303             : }
     304             : 
     305           1 : odbc::Time OGRHanaFeatureReader::GetFieldAsTime(int fieldIndex) const
     306             : {
     307           1 :     if (IsFieldSet(fieldIndex))
     308             :     {
     309           0 :         int year = 0;
     310           0 :         int month = 0;
     311           0 :         int day = 0;
     312           0 :         int hour = 0;
     313           0 :         int minute = 0;
     314           0 :         int timeZoneFlag = 0;
     315           0 :         float second = 0.0f;
     316           0 :         feature_.GetFieldAsDateTime(fieldIndex, &year, &month, &day, &hour,
     317             :                                     &minute, &second, &timeZoneFlag);
     318             :         return odbc::makeNullable<odbc::time>(hour, minute,
     319           0 :                                               static_cast<int>(round(second)));
     320             :     }
     321             : 
     322           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     323           1 :     if (defaultValue == nullptr)
     324           0 :         return odbc::Time();
     325             : 
     326           1 :     if (EQUAL(defaultValue, "CURRENT_TIME"))
     327             :     {
     328           0 :         std::time_t t = std::time(nullptr);
     329           0 :         tm *now = std::localtime(&t);
     330           0 :         if (now == nullptr)
     331           0 :             return odbc::Time();
     332           0 :         return odbc::makeNullable<odbc::time>(now->tm_hour, now->tm_min,
     333           0 :                                               now->tm_sec);
     334             :     }
     335             : 
     336           1 :     int hour = 0;
     337           1 :     int minute = 0;
     338           1 :     int second = 0;
     339           1 :     sscanf(defaultValue, "'%02d:%02d:%02d'", &hour, &minute, &second);
     340           1 :     return odbc::makeNullable<odbc::time>(hour, minute, second);
     341             : }
     342             : 
     343           4 : odbc::Timestamp OGRHanaFeatureReader::GetFieldAsTimestamp(int fieldIndex) const
     344             : {
     345           4 :     if (IsFieldSet(fieldIndex))
     346             :     {
     347           3 :         int year = 0;
     348           3 :         int month = 0;
     349           3 :         int day = 0;
     350           3 :         int hour = 0;
     351           3 :         int minute = 0;
     352           3 :         float secondWithMillisecond = 0.0f;
     353           3 :         int timeZoneFlag = 0;
     354           3 :         feature_.GetFieldAsDateTime(fieldIndex, &year, &month, &day, &hour,
     355             :                                     &minute, &secondWithMillisecond,
     356             :                                     &timeZoneFlag);
     357           3 :         double seconds = 0.0;
     358             :         double milliseconds =
     359           3 :             std::modf(static_cast<double>(secondWithMillisecond), &seconds);
     360           3 :         int second = static_cast<int>(std::floor(seconds));
     361           3 :         int millisecond = static_cast<int>(std::floor(milliseconds * 1000));
     362             : 
     363           3 :         if (!(timeZoneFlag == 0 || timeZoneFlag == 100 || timeZoneFlag == 1))
     364             :         {
     365             :             struct tm time;
     366           1 :             time.tm_year = year - 1900;
     367           1 :             time.tm_mon = month - 1;
     368           1 :             time.tm_mday = day;
     369           1 :             time.tm_hour = hour;
     370           1 :             time.tm_min = minute;
     371           1 :             time.tm_sec = second;
     372           1 :             GIntBig dt = CPLYMDHMSToUnixTime(&time);
     373           1 :             const int tzoffset = std::abs(timeZoneFlag - 100) * 15;
     374           1 :             dt -= tzoffset * 60;
     375           1 :             CPLUnixTimeToYMDHMS(dt, &time);
     376           1 :             year = time.tm_year + 1900;
     377           1 :             month = time.tm_mon + 1;
     378           1 :             day = time.tm_mday;
     379           1 :             hour = time.tm_hour;
     380           1 :             minute = time.tm_min;
     381           1 :             second = time.tm_sec;
     382             :         }
     383             : 
     384             :         return odbc::makeNullable<odbc::timestamp>(year, month, day, hour,
     385           3 :                                                    minute, second, millisecond);
     386             :     }
     387             : 
     388           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     389           1 :     if (defaultValue == nullptr)
     390           0 :         return odbc::Timestamp();
     391             : 
     392           1 :     if (EQUAL(defaultValue, "CURRENT_TIMESTAMP"))
     393             :     {
     394           0 :         time_t t = std::time(nullptr);
     395           0 :         tm *now = std::localtime(&t);
     396           0 :         if (now == nullptr)
     397           0 :             return odbc::Timestamp();
     398             :         return odbc::makeNullable<odbc::timestamp>(
     399           0 :             now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour,
     400           0 :             now->tm_min, now->tm_sec, 0);
     401             :     }
     402             : 
     403           1 :     int year = 0;
     404           1 :     int month = 0;
     405           1 :     int day = 0;
     406           1 :     int hour = 0;
     407           1 :     int minute = 0;
     408           1 :     int second = 0;
     409           1 :     int millisecond = 0;
     410             : 
     411           1 :     if (strchr(defaultValue, '.') == nullptr)
     412           0 :         sscanf(defaultValue, "'%04d/%02d/%02d %02d:%02d:%02d'", &year, &month,
     413             :                &day, &hour, &minute, &second);
     414             :     else
     415           1 :         sscanf(defaultValue, "'%04d/%02d/%02d %02d:%02d:%02d.%03d'", &year,
     416             :                &month, &day, &hour, &minute, &second, &millisecond);
     417             : 
     418             :     return odbc::makeNullable<odbc::timestamp>(year, month, day, hour, minute,
     419           1 :                                                second, millisecond);
     420             : }
     421             : 
     422           1 : Binary OGRHanaFeatureReader::GetFieldAsBinary(int fieldIndex) const
     423             : {
     424           1 :     if (IsFieldSet(fieldIndex))
     425             :     {
     426           0 :         int size = 0;
     427           0 :         GByte *data = feature_.GetFieldAsBinary(fieldIndex, &size);
     428           0 :         return {data, static_cast<std::size_t>(size)};
     429             :     }
     430             : 
     431           1 :     const char *defaultValue = GetDefaultValue(fieldIndex);
     432           1 :     if (defaultValue == nullptr)
     433           0 :         return {nullptr, 0U};
     434             : 
     435             :     return {const_cast<GByte *>(reinterpret_cast<const GByte *>(defaultValue)),
     436           1 :             std::strlen(defaultValue)};
     437             : }
     438             : 
     439           1 : odbc::String OGRHanaFeatureReader::GetFieldAsIntArray(int fieldIndex) const
     440             : {
     441           1 :     if (!IsFieldSet(fieldIndex))
     442           1 :         return odbc::String();
     443             : 
     444             :     int numElements;
     445             :     const int *values =
     446           0 :         feature_.GetFieldAsIntegerList(fieldIndex, &numElements);
     447           0 :     return CreateStringFromValues<int>(values, numElements, &std::to_string);
     448             : }
     449             : 
     450           1 : odbc::String OGRHanaFeatureReader::GetFieldAsBigIntArray(int fieldIndex) const
     451             : {
     452           1 :     if (!IsFieldSet(fieldIndex))
     453           1 :         return odbc::String();
     454             : 
     455             :     int numElements;
     456             :     const GIntBig *values =
     457           0 :         feature_.GetFieldAsInteger64List(fieldIndex, &numElements);
     458             :     return CreateStringFromValues<GIntBig>(values, numElements,
     459           0 :                                            &std::to_string);
     460             : }
     461             : 
     462           0 : odbc::String OGRHanaFeatureReader::GetFieldAsRealArray(int fieldIndex) const
     463             : {
     464           0 :     if (!IsFieldSet(fieldIndex))
     465           0 :         return odbc::String();
     466             : 
     467             :     int numElements;
     468             :     const double *values =
     469           0 :         feature_.GetFieldAsDoubleList(fieldIndex, &numElements);
     470             :     return CreateStringFromValues<double>(
     471             :         values, numElements,
     472           0 :         [](double value) -> std::string
     473             :         {
     474           0 :             return std::isnan(value)
     475             :                        ? "NULL"
     476           0 :                        : std::to_string(static_cast<float>(value));
     477           0 :         });
     478             : }
     479             : 
     480           1 : odbc::String OGRHanaFeatureReader::GetFieldAsDoubleArray(int fieldIndex) const
     481             : {
     482           1 :     if (!IsFieldSet(fieldIndex))
     483           1 :         return odbc::String();
     484             : 
     485             :     int numElements;
     486             :     const double *values =
     487           0 :         feature_.GetFieldAsDoubleList(fieldIndex, &numElements);
     488             :     return CreateStringFromValues<double>(
     489             :         values, numElements,
     490           0 :         [](double value) -> std::string
     491           0 :         { return std::isnan(value) ? "NULL" : std::to_string(value); });
     492             : }
     493             : 
     494           1 : odbc::String OGRHanaFeatureReader::GetFieldAsStringArray(int fieldIndex) const
     495             : {
     496           1 :     if (!IsFieldSet(fieldIndex))
     497           1 :         return odbc::String();
     498             : 
     499           0 :     char **items = feature_.GetFieldAsStringList(fieldIndex);
     500           0 :     if (items == nullptr)
     501           0 :         return odbc::String();
     502             : 
     503           0 :     std::ostringstream os;
     504           0 :     bool firstItem = true;
     505           0 :     while (items && *items)
     506             :     {
     507           0 :         if (!firstItem)
     508           0 :             os << ARRAY_VALUES_DELIMITER;
     509             : 
     510           0 :         char *itemValue = *items;
     511           0 :         if (*itemValue != '\0')
     512             :         {
     513           0 :             os << '\'';
     514           0 :             while (*itemValue)
     515             :             {
     516           0 :                 if (*itemValue == '\'')
     517           0 :                     os << "'";
     518           0 :                 os << *itemValue;
     519           0 :                 ++itemValue;
     520             :             }
     521           0 :             os << '\'';
     522             :         }
     523             : 
     524           0 :         ++items;
     525           0 :         firstItem = false;
     526             :     }
     527             : 
     528           0 :     return odbc::String(os.str());
     529             : }
     530             : 
     531         169 : const char *OGRHanaFeatureReader::GetDefaultValue(int fieldIndex) const
     532             : {
     533         169 :     const OGRFieldDefn *fieldDef = feature_.GetFieldDefnRef(fieldIndex);
     534         169 :     return fieldDef->GetDefault();
     535             : }
     536             : 
     537         517 : bool OGRHanaFeatureReader::IsFieldSet(int fieldIndex) const
     538             : {
     539         517 :     return feature_.IsFieldSet(fieldIndex) && !feature_.IsFieldNull(fieldIndex);
     540             : }
     541             : 
     542             : }  // namespace OGRHANA

Generated by: LCOV version 1.14