LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdb_fielddomain.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 179 238 75.2 %
Date: 2024-11-21 22:18:42 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB OGR driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifndef FILEGDB_FIELDDOMAIN_H
      14             : #define FILEGDB_FIELDDOMAIN_H
      15             : 
      16             : #include "cpl_minixml.h"
      17             : #include "filegdb_gdbtoogrfieldtype.h"
      18             : #include "ogr_p.h"
      19             : 
      20             : /************************************************************************/
      21             : /*                      ParseXMLFieldDomainDef()                        */
      22             : /************************************************************************/
      23             : 
      24             : inline std::unique_ptr<OGRFieldDomain>
      25        1101 : ParseXMLFieldDomainDef(const std::string &domainDef)
      26             : {
      27        2202 :     CPLXMLTreeCloser oTree(CPLParseXMLString(domainDef.c_str()));
      28        1101 :     if (!oTree.get())
      29             :     {
      30           0 :         return nullptr;
      31             :     }
      32        1101 :     const CPLXMLNode *psDomain = CPLGetXMLNode(oTree.get(), "=esri:Domain");
      33        1101 :     if (psDomain == nullptr)
      34             :     {
      35             :         // esri: namespace prefix omitted when called from the FileGDB driver
      36        1101 :         psDomain = CPLGetXMLNode(oTree.get(), "=Domain");
      37             :     }
      38        1101 :     bool bIsCodedValueDomain = false;
      39        1101 :     if (psDomain == nullptr)
      40             :     {
      41             :         // Also sometimes found...
      42        1099 :         psDomain = CPLGetXMLNode(oTree.get(), "=esri:CodedValueDomain");
      43        1099 :         if (psDomain)
      44           1 :             bIsCodedValueDomain = true;
      45             :     }
      46        1101 :     if (psDomain == nullptr)
      47             :     {
      48             :         // Also sometimes found...
      49        1098 :         psDomain = CPLGetXMLNode(oTree.get(), "=typens:GPCodedValueDomain2");
      50        1098 :         if (psDomain)
      51          20 :             bIsCodedValueDomain = true;
      52             :     }
      53        1101 :     if (psDomain == nullptr)
      54             :     {
      55             :         // Also sometimes found...
      56        1078 :         psDomain = CPLGetXMLNode(oTree.get(), "=GPCodedValueDomain2");
      57        1078 :         if (psDomain)
      58        1056 :             bIsCodedValueDomain = true;
      59             :     }
      60        1101 :     bool bIsRangeDomain = false;
      61        1101 :     if (psDomain == nullptr)
      62             :     {
      63             :         // Also sometimes found...
      64          22 :         psDomain = CPLGetXMLNode(oTree.get(), "=esri:RangeDomain");
      65          22 :         if (psDomain)
      66           3 :             bIsRangeDomain = true;
      67             :     }
      68        1101 :     if (psDomain == nullptr)
      69             :     {
      70             :         // Also sometimes found...
      71          19 :         psDomain = CPLGetXMLNode(oTree.get(), "=typens:GPRangeDomain2");
      72          19 :         if (psDomain)
      73          19 :             bIsRangeDomain = true;
      74             :     }
      75        1101 :     if (psDomain == nullptr)
      76             :     {
      77             :         // Also sometimes found...
      78           0 :         psDomain = CPLGetXMLNode(oTree.get(), "=GPRangeDomain2");
      79           0 :         if (psDomain)
      80           0 :             bIsRangeDomain = true;
      81             :     }
      82        1101 :     if (psDomain == nullptr)
      83             :     {
      84           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find root 'Domain' node");
      85           0 :         return nullptr;
      86             :     }
      87        1101 :     const char *pszType = CPLGetXMLValue(psDomain, "xsi:type", "");
      88        1101 :     const char *pszName = CPLGetXMLValue(psDomain, "DomainName", "");
      89        1101 :     const char *pszDescription = CPLGetXMLValue(psDomain, "Description", "");
      90        1101 :     const char *pszFieldType = CPLGetXMLValue(psDomain, "FieldType", "");
      91        1101 :     OGRFieldType eFieldType = OFTString;
      92        1101 :     OGRFieldSubType eSubType = OFSTNone;
      93        1101 :     if (!GDBToOGRFieldType(pszFieldType, &eFieldType, &eSubType))
      94             :     {
      95           0 :         return nullptr;
      96             :     }
      97             : 
      98        1101 :     std::unique_ptr<OGRFieldDomain> domain;
      99        1101 :     if (bIsCodedValueDomain || strcmp(pszType, "esri:CodedValueDomain") == 0)
     100             :     {
     101             :         const CPLXMLNode *psCodedValues =
     102        1079 :             CPLGetXMLNode(psDomain, "CodedValues");
     103        1079 :         if (psCodedValues == nullptr)
     104             :         {
     105           0 :             return nullptr;
     106             :         }
     107        1079 :         std::vector<OGRCodedValue> asValues;
     108       19185 :         for (const CPLXMLNode *psIter = psCodedValues->psChild; psIter;
     109       18106 :              psIter = psIter->psNext)
     110             :         {
     111       18106 :             if (psIter->eType == CXT_Element &&
     112       17027 :                 strcmp(psIter->pszValue, "CodedValue") == 0)
     113             :             {
     114             :                 OGRCodedValue cv;
     115       17027 :                 cv.pszCode = CPLStrdup(CPLGetXMLValue(psIter, "Code", ""));
     116       17027 :                 cv.pszValue = CPLStrdup(CPLGetXMLValue(psIter, "Name", ""));
     117       17027 :                 asValues.emplace_back(cv);
     118             :             }
     119             :         }
     120        2158 :         domain.reset(new OGRCodedFieldDomain(pszName, pszDescription,
     121             :                                              eFieldType, eSubType,
     122        2158 :                                              std::move(asValues)));
     123             :     }
     124          22 :     else if (bIsRangeDomain || strcmp(pszType, "esri:RangeDomain") == 0)
     125             :     {
     126          22 :         if (eFieldType != OFTInteger && eFieldType != OFTInteger64 &&
     127           9 :             eFieldType != OFTReal && eFieldType != OFTDateTime)
     128             :         {
     129           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     130             :                      "Unsupported field type for range domain: %s",
     131             :                      pszFieldType);
     132           0 :             return nullptr;
     133             :         }
     134          22 :         const char *pszMinValue = CPLGetXMLValue(psDomain, "MinValue", "");
     135          22 :         const char *pszMaxValue = CPLGetXMLValue(psDomain, "MaxValue", "");
     136             :         OGRField sMin;
     137             :         OGRField sMax;
     138          22 :         OGR_RawField_SetUnset(&sMin);
     139          22 :         OGR_RawField_SetUnset(&sMax);
     140          22 :         if (eFieldType == OFTInteger)
     141             :         {
     142          13 :             sMin.Integer = atoi(pszMinValue);
     143          13 :             sMax.Integer = atoi(pszMaxValue);
     144             :         }
     145           9 :         else if (eFieldType == OFTInteger64)
     146             :         {
     147           0 :             sMin.Integer64 = CPLAtoGIntBig(pszMinValue);
     148           0 :             sMax.Integer64 = CPLAtoGIntBig(pszMaxValue);
     149             :         }
     150           9 :         else if (eFieldType == OFTReal)
     151             :         {
     152           0 :             sMin.Real = CPLAtof(pszMinValue);
     153           0 :             sMax.Real = CPLAtof(pszMaxValue);
     154             :         }
     155           9 :         else if (eFieldType == OFTDateTime)
     156             :         {
     157           9 :             if (!OGRParseXMLDateTime(pszMinValue, &sMin))
     158             :             {
     159           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid MinValue: %s",
     160             :                          pszMinValue);
     161           0 :                 return nullptr;
     162             :             }
     163           9 :             if (!OGRParseXMLDateTime(pszMaxValue, &sMax))
     164             :             {
     165           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid MaxValue: %s",
     166             :                          pszMaxValue);
     167           0 :                 return nullptr;
     168             :             }
     169             :         }
     170          44 :         domain.reset(new OGRRangeFieldDomain(pszName, pszDescription,
     171             :                                              eFieldType, eSubType, sMin, true,
     172          44 :                                              sMax, true));
     173             :     }
     174             :     else
     175             :     {
     176           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     177             :                  "Unsupported type of File Geodatabase domain: %s", pszType);
     178           0 :         return nullptr;
     179             :     }
     180             : 
     181             :     const char *pszMergePolicy =
     182        1101 :         CPLGetXMLValue(psDomain, "MergePolicy", "esriMPTDefaultValue");
     183        1101 :     if (EQUAL(pszMergePolicy, "esriMPTDefaultValue"))
     184             :     {
     185        1101 :         domain->SetMergePolicy(OFDMP_DEFAULT_VALUE);
     186             :     }
     187           0 :     else if (EQUAL(pszMergePolicy, "esriMPTSumValues"))
     188             :     {
     189           0 :         domain->SetMergePolicy(OFDMP_SUM);
     190             :     }
     191           0 :     else if (EQUAL(pszMergePolicy, "esriMPTAreaWeighted"))
     192             :     {
     193           0 :         domain->SetMergePolicy(OFDMP_GEOMETRY_WEIGHTED);
     194             :     }
     195             : 
     196             :     const char *pszSplitPolicy =
     197        1101 :         CPLGetXMLValue(psDomain, "SplitPolicy", "esriSPTDefaultValue");
     198        1101 :     if (EQUAL(pszSplitPolicy, "esriSPTDefaultValue"))
     199             :     {
     200        1089 :         domain->SetSplitPolicy(OFDSP_DEFAULT_VALUE);
     201             :     }
     202          12 :     else if (EQUAL(pszSplitPolicy, "esriSPTDuplicate"))
     203             :     {
     204          12 :         domain->SetSplitPolicy(OFDSP_DUPLICATE);
     205             :     }
     206           0 :     else if (EQUAL(pszSplitPolicy, "esriSPTGeometryRatio"))
     207             :     {
     208           0 :         domain->SetSplitPolicy(OFDSP_GEOMETRY_RATIO);
     209             :     }
     210             : 
     211        1101 :     return domain;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                      BuildXMLFieldDomainDef()                        */
     216             : /************************************************************************/
     217             : 
     218          15 : inline std::string BuildXMLFieldDomainDef(const OGRFieldDomain *poDomain,
     219             :                                           bool bForFileGDBSDK,
     220             :                                           std::string &failureReason)
     221             : {
     222          30 :     std::string osNS = "esri";
     223          15 :     const char *pszRootElt = "esri:Domain";
     224          15 :     if (!bForFileGDBSDK)
     225             :     {
     226           7 :         switch (poDomain->GetDomainType())
     227             :         {
     228           4 :             case OFDT_CODED:
     229             :             {
     230           4 :                 pszRootElt = "typens:GPCodedValueDomain2";
     231           4 :                 break;
     232             :             }
     233             : 
     234           3 :             case OFDT_RANGE:
     235             :             {
     236           3 :                 pszRootElt = "typens:GPRangeDomain2";
     237           3 :                 break;
     238             :             }
     239             : 
     240           0 :             case OFDT_GLOB:
     241             :             {
     242             :                 failureReason =
     243           0 :                     "Glob field domain not handled for FileGeoDatabase";
     244           0 :                 return std::string();
     245             :             }
     246             :         }
     247           7 :         osNS = "typens";
     248             :     }
     249             : 
     250          30 :     CPLXMLTreeCloser oTree(CPLCreateXMLNode(nullptr, CXT_Element, pszRootElt));
     251          15 :     CPLXMLNode *psRoot = oTree.get();
     252             : 
     253          15 :     switch (poDomain->GetDomainType())
     254             :     {
     255           9 :         case OFDT_CODED:
     256             :         {
     257           9 :             CPLAddXMLAttributeAndValue(psRoot, "xsi:type",
     258             :                                        bForFileGDBSDK
     259             :                                            ? "esri:CodedValueDomain"
     260             :                                            : "typens:GPCodedValueDomain2");
     261           9 :             break;
     262             :         }
     263             : 
     264           6 :         case OFDT_RANGE:
     265             :         {
     266           6 :             CPLAddXMLAttributeAndValue(
     267             :                 psRoot, "xsi:type",
     268             :                 bForFileGDBSDK ? "esri:RangeDomain" : "typens:GPRangeDomain2");
     269           6 :             break;
     270             :         }
     271             : 
     272           0 :         case OFDT_GLOB:
     273             :         {
     274           0 :             failureReason = "Glob field domain not handled for FileGeoDatabase";
     275           0 :             return std::string();
     276             :         }
     277             :     }
     278             : 
     279          15 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xsi",
     280             :                                "http://www.w3.org/2001/XMLSchema-instance");
     281          15 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xs",
     282             :                                "http://www.w3.org/2001/XMLSchema");
     283          15 :     CPLAddXMLAttributeAndValue(psRoot, ("xmlns:" + osNS).c_str(),
     284             :                                "http://www.esri.com/schemas/ArcGIS/10.1");
     285             : 
     286          15 :     CPLCreateXMLElementAndValue(psRoot, "DomainName",
     287          15 :                                 poDomain->GetName().c_str());
     288          15 :     if (poDomain->GetFieldType() == OFTInteger)
     289             :     {
     290          14 :         if (poDomain->GetFieldSubType() == OFSTInt16)
     291           6 :             CPLCreateXMLElementAndValue(psRoot, "FieldType",
     292             :                                         "esriFieldTypeSmallInteger");
     293             :         else
     294           8 :             CPLCreateXMLElementAndValue(psRoot, "FieldType",
     295             :                                         "esriFieldTypeInteger");
     296             :     }
     297           1 :     else if (poDomain->GetFieldType() == OFTReal)
     298             :     {
     299           0 :         if (poDomain->GetFieldSubType() == OFSTFloat32)
     300           0 :             CPLCreateXMLElementAndValue(psRoot, "FieldType",
     301             :                                         "esriFieldTypeSingle");
     302             :         else
     303           0 :             CPLCreateXMLElementAndValue(psRoot, "FieldType",
     304             :                                         "esriFieldTypeDouble");
     305             :     }
     306           1 :     else if (poDomain->GetFieldType() == OFTString)
     307             :     {
     308           0 :         CPLCreateXMLElementAndValue(psRoot, "FieldType", "esriFieldTypeString");
     309             :     }
     310           1 :     else if (poDomain->GetFieldType() == OFTDateTime)
     311             :     {
     312           1 :         CPLCreateXMLElementAndValue(psRoot, "FieldType", "esriFieldTypeDate");
     313             :     }
     314             :     else
     315             :     {
     316           0 :         failureReason = "Unsupported field type for FileGeoDatabase domain";
     317           0 :         return std::string();
     318             :     }
     319             : 
     320          15 :     switch (poDomain->GetMergePolicy())
     321             :     {
     322          15 :         case OFDMP_DEFAULT_VALUE:
     323          15 :             CPLCreateXMLElementAndValue(psRoot, "MergePolicy",
     324             :                                         "esriMPTDefaultValue");
     325          15 :             break;
     326           0 :         case OFDMP_SUM:
     327           0 :             CPLCreateXMLElementAndValue(psRoot, "MergePolicy",
     328             :                                         "esriMPTSumValues");
     329           0 :             break;
     330           0 :         case OFDMP_GEOMETRY_WEIGHTED:
     331           0 :             CPLCreateXMLElementAndValue(psRoot, "MergePolicy",
     332             :                                         "esriMPTAreaWeighted");
     333           0 :             break;
     334             :     }
     335             : 
     336          15 :     switch (poDomain->GetSplitPolicy())
     337             :     {
     338          15 :         case OFDSP_DEFAULT_VALUE:
     339          15 :             CPLCreateXMLElementAndValue(psRoot, "SplitPolicy",
     340             :                                         "esriSPTDefaultValue");
     341          15 :             break;
     342           0 :         case OFDSP_DUPLICATE:
     343           0 :             CPLCreateXMLElementAndValue(psRoot, "SplitPolicy",
     344             :                                         "esriSPTDuplicate");
     345           0 :             break;
     346           0 :         case OFDSP_GEOMETRY_RATIO:
     347           0 :             CPLCreateXMLElementAndValue(psRoot, "SplitPolicy",
     348             :                                         "esriSPTGeometryRatio");
     349           0 :             break;
     350             :     }
     351             : 
     352          15 :     CPLCreateXMLElementAndValue(psRoot, "Description",
     353          15 :                                 poDomain->GetDescription().c_str());
     354          15 :     CPLCreateXMLElementAndValue(psRoot, "Owner", "");
     355             : 
     356          76 :     const auto AddFieldTypeAsXSIType = [&poDomain](CPLXMLNode *psParent)
     357             :     {
     358          36 :         if (poDomain->GetFieldType() == OFTInteger)
     359             :         {
     360          34 :             if (poDomain->GetFieldSubType() == OFSTInt16)
     361          12 :                 CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:short");
     362             :             else
     363          22 :                 CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:int");
     364             :         }
     365           2 :         else if (poDomain->GetFieldType() == OFTReal)
     366             :         {
     367           0 :             if (poDomain->GetFieldSubType() == OFSTFloat32)
     368           0 :                 CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:float");
     369             :             else
     370           0 :                 CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:double");
     371             :         }
     372           2 :         else if (poDomain->GetFieldType() == OFTString)
     373             :         {
     374           0 :             CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:string");
     375             :         }
     376           2 :         else if (poDomain->GetFieldType() == OFTDateTime)
     377             :         {
     378           2 :             CPLAddXMLAttributeAndValue(psParent, "xsi:type", "xs:dateTime");
     379             :         }
     380          51 :     };
     381             : 
     382          15 :     switch (poDomain->GetDomainType())
     383             :     {
     384           9 :         case OFDT_CODED:
     385             :         {
     386             :             auto psCodedValues =
     387           9 :                 CPLCreateXMLNode(psRoot, CXT_Element, "CodedValues");
     388           9 :             CPLAddXMLAttributeAndValue(psCodedValues, "xsi:type",
     389          18 :                                        (osNS + ":ArrayOfCodedValue").c_str());
     390             : 
     391             :             auto poCodedDomain =
     392           9 :                 cpl::down_cast<const OGRCodedFieldDomain *>(poDomain);
     393             :             const OGRCodedValue *psEnumeration =
     394           9 :                 poCodedDomain->GetEnumeration();
     395          33 :             for (; psEnumeration->pszCode != nullptr; ++psEnumeration)
     396             :             {
     397             :                 auto psCodedValue =
     398          24 :                     CPLCreateXMLNode(psCodedValues, CXT_Element, "CodedValue");
     399          24 :                 CPLAddXMLAttributeAndValue(psCodedValue, "xsi:type",
     400          48 :                                            (osNS + ":CodedValue").c_str());
     401          24 :                 CPLCreateXMLElementAndValue(
     402             :                     psCodedValue, "Name",
     403          24 :                     psEnumeration->pszValue ? psEnumeration->pszValue : "");
     404             : 
     405             :                 auto psCode =
     406          24 :                     CPLCreateXMLNode(psCodedValue, CXT_Element, "Code");
     407          24 :                 AddFieldTypeAsXSIType(psCode);
     408          24 :                 CPLCreateXMLNode(psCode, CXT_Text, psEnumeration->pszCode);
     409             :             }
     410           9 :             break;
     411             :         }
     412             : 
     413           6 :         case OFDT_RANGE:
     414             :         {
     415             :             auto poRangeDomain =
     416           6 :                 cpl::down_cast<const OGRRangeFieldDomain *>(poDomain);
     417             : 
     418             :             const auto SerializeMinOrMax =
     419          12 :                 [&AddFieldTypeAsXSIType, &poDomain,
     420          42 :                  psRoot](const char *pszElementName, const OGRField &oValue)
     421             :             {
     422          12 :                 if (!OGR_RawField_IsUnset(&oValue))
     423             :                 {
     424             :                     auto psValue =
     425          12 :                         CPLCreateXMLNode(psRoot, CXT_Element, pszElementName);
     426          12 :                     AddFieldTypeAsXSIType(psValue);
     427          12 :                     if (poDomain->GetFieldType() == OFTInteger)
     428             :                     {
     429          10 :                         CPLCreateXMLNode(psValue, CXT_Text,
     430          10 :                                          CPLSPrintf("%d", oValue.Integer));
     431             :                     }
     432           2 :                     else if (poDomain->GetFieldType() == OFTReal)
     433             :                     {
     434           0 :                         CPLCreateXMLNode(psValue, CXT_Text,
     435           0 :                                          CPLSPrintf("%.18g", oValue.Real));
     436             :                     }
     437           2 :                     else if (poDomain->GetFieldType() == OFTString)
     438             :                     {
     439           0 :                         CPLCreateXMLNode(psValue, CXT_Text, oValue.String);
     440             :                     }
     441           2 :                     else if (poDomain->GetFieldType() == OFTDateTime)
     442             :                     {
     443           2 :                         CPLCreateXMLNode(
     444             :                             psValue, CXT_Text,
     445             :                             CPLSPrintf(
     446             :                                 "%04d-%02d-%02dT%02d:%02d:%02d",
     447           2 :                                 oValue.Date.Year, oValue.Date.Month,
     448           2 :                                 oValue.Date.Day, oValue.Date.Hour,
     449           2 :                                 oValue.Date.Minute,
     450           2 :                                 static_cast<int>(oValue.Date.Second + 0.5)));
     451             :                     }
     452             :                 }
     453          18 :             };
     454             : 
     455           6 :             bool bIsInclusiveOut = false;
     456           6 :             const OGRField &oMax = poRangeDomain->GetMax(bIsInclusiveOut);
     457           6 :             SerializeMinOrMax("MaxValue", oMax);
     458             : 
     459           6 :             bIsInclusiveOut = false;
     460           6 :             const OGRField &oMin = poRangeDomain->GetMin(bIsInclusiveOut);
     461           6 :             SerializeMinOrMax("MinValue", oMin);
     462             : 
     463           6 :             break;
     464             :         }
     465             : 
     466           0 :         case OFDT_GLOB:
     467             :         {
     468           0 :             CPLAssert(false);
     469             :             break;
     470             :         }
     471             :     }
     472             : 
     473          15 :     char *pszXML = CPLSerializeXMLTree(oTree.get());
     474          30 :     const std::string osXML(pszXML);
     475          15 :     CPLFree(pszXML);
     476          15 :     return osXML;
     477             : }
     478             : 
     479             : #endif  // FILEGDB_FIELDDOMAIN_H

Generated by: LCOV version 1.14