LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdb_relationship.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 281 308 91.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 FileGDB relationship handling.
       5             :  * Author:   Nyall Dawson, <nyall dot dawson at gmail dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Nyall Dawson <nyall dot dawson at gmail dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifndef FILEGDB_RELATIONSHIP_H
      14             : #define FILEGDB_RELATIONSHIP_H
      15             : 
      16             : #include "cpl_minixml.h"
      17             : #include "filegdb_gdbtoogrfieldtype.h"
      18             : #include "gdal.h"
      19             : 
      20             : /************************************************************************/
      21             : /*                      ParseXMLFieldDomainDef()                        */
      22             : /************************************************************************/
      23             : 
      24             : inline std::unique_ptr<GDALRelationship>
      25         230 : ParseXMLRelationshipDef(const std::string &domainDef)
      26             : {
      27         460 :     CPLXMLTreeCloser oTree(CPLParseXMLString(domainDef.c_str()));
      28         230 :     if (!oTree.get())
      29             :     {
      30           0 :         return nullptr;
      31             :     }
      32             : 
      33             :     const CPLXMLNode *psRelationship =
      34         230 :         CPLGetXMLNode(oTree.get(), "=DERelationshipClassInfo");
      35         230 :     if (psRelationship == nullptr)
      36             :     {
      37           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      38             :                  "Cannot find root 'Relationship' node");
      39           0 :         return nullptr;
      40             :     }
      41             : 
      42         230 :     const char *pszName = CPLGetXMLValue(psRelationship, "Name", "");
      43             : 
      44             :     const char *pszOriginTableName =
      45         230 :         CPLGetXMLValue(psRelationship, "OriginClassNames.Name", nullptr);
      46         230 :     if (pszOriginTableName == nullptr)
      47             :     {
      48           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      49             :                  "Cannot find OriginClassName table node");
      50           0 :         return nullptr;
      51             :     }
      52             : 
      53             :     const char *pszDestinationTableName =
      54         230 :         CPLGetXMLValue(psRelationship, "DestinationClassNames.Name", nullptr);
      55         230 :     if (pszDestinationTableName == nullptr)
      56             :     {
      57           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      58             :                  "Cannot find DestinationClassNames table node");
      59           0 :         return nullptr;
      60             :     }
      61             : 
      62             :     const char *pszCardinality =
      63         230 :         CPLGetXMLValue(psRelationship, "Cardinality", "");
      64         230 :     if (pszCardinality == nullptr)
      65             :     {
      66           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Cardinality node");
      67           0 :         return nullptr;
      68             :     }
      69             : 
      70         230 :     GDALRelationshipCardinality eCardinality = GRC_ONE_TO_MANY;
      71         230 :     if (EQUAL(pszCardinality, "esriRelCardinalityOneToOne"))
      72             :     {
      73         113 :         eCardinality = GRC_ONE_TO_ONE;
      74             :     }
      75         117 :     else if (EQUAL(pszCardinality, "esriRelCardinalityOneToMany"))
      76             :     {
      77          75 :         eCardinality = GRC_ONE_TO_MANY;
      78             :     }
      79          42 :     else if (EQUAL(pszCardinality, "esriRelCardinalityManyToMany"))
      80             :     {
      81          42 :         eCardinality = GRC_MANY_TO_MANY;
      82             :     }
      83             :     else
      84             :     {
      85           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown cardinality: %s",
      86             :                  pszCardinality);
      87           0 :         return nullptr;
      88             :     }
      89             : 
      90             :     std::unique_ptr<GDALRelationship> poRelationship(new GDALRelationship(
      91         690 :         pszName, pszOriginTableName, pszDestinationTableName, eCardinality));
      92             : 
      93         230 :     if (eCardinality == GRC_MANY_TO_MANY)
      94             :     {
      95             :         // seems to be that the middle table name always follows the
      96             :         // relationship name?
      97          42 :         poRelationship->SetMappingTableName(pszName);
      98             :     }
      99             : 
     100         460 :     std::vector<std::string> aosOriginKeys;
     101         460 :     std::vector<std::string> aosMappingOriginKeys;
     102         460 :     std::vector<std::string> aosDestinationKeys;
     103         460 :     std::vector<std::string> aosMappingDestinationKeys;
     104             : 
     105             :     const CPLXMLNode *psOriginClassKeys =
     106         230 :         CPLGetXMLNode(psRelationship, "OriginClassKeys");
     107         230 :     if (psOriginClassKeys == nullptr)
     108             :     {
     109           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     110             :                  "Cannot find OriginClassKeys node");
     111           0 :         return nullptr;
     112             :     }
     113         920 :     for (const CPLXMLNode *psIter = psOriginClassKeys->psChild; psIter;
     114         690 :          psIter = psIter->psNext)
     115             :     {
     116         690 :         if (psIter->eType == CXT_Element &&
     117         460 :             strcmp(psIter->pszValue, "RelationshipClassKey") == 0)
     118             :         {
     119             :             const char *pszObjectKeyName =
     120         460 :                 CPLGetXMLValue(psIter, "ObjectKeyName", "");
     121         460 :             if (pszObjectKeyName == nullptr)
     122             :             {
     123           0 :                 continue;
     124             :             }
     125             : 
     126         460 :             const char *pszKeyRole = CPLGetXMLValue(psIter, "KeyRole", "");
     127         460 :             if (pszKeyRole == nullptr)
     128             :             {
     129           0 :                 continue;
     130             :             }
     131         460 :             if (EQUAL(pszKeyRole, "esriRelKeyRoleOriginPrimary"))
     132             :             {
     133         230 :                 aosOriginKeys.emplace_back(pszObjectKeyName);
     134             :             }
     135         230 :             else if (EQUAL(pszKeyRole, "esriRelKeyRoleOriginForeign"))
     136             :             {
     137         230 :                 if (eCardinality == GRC_MANY_TO_MANY)
     138          42 :                     aosMappingOriginKeys.emplace_back(pszObjectKeyName);
     139             :                 else
     140         188 :                     aosDestinationKeys.emplace_back(pszObjectKeyName);
     141             :             }
     142             :             else
     143             :             {
     144           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Unknown KeyRole: %s",
     145             :                          pszKeyRole);
     146           0 :                 return nullptr;
     147             :             }
     148             :         }
     149             :     }
     150             : 
     151             :     const CPLXMLNode *psDestinationClassKeys =
     152         230 :         CPLGetXMLNode(psRelationship, "DestinationClassKeys");
     153         230 :     if (psDestinationClassKeys != nullptr)
     154             :     {
     155         236 :         for (const CPLXMLNode *psIter = psDestinationClassKeys->psChild; psIter;
     156         177 :              psIter = psIter->psNext)
     157             :         {
     158         177 :             if (psIter->eType == CXT_Element &&
     159         118 :                 strcmp(psIter->pszValue, "RelationshipClassKey") == 0)
     160             :             {
     161             :                 const char *pszObjectKeyName =
     162         118 :                     CPLGetXMLValue(psIter, "ObjectKeyName", "");
     163         118 :                 if (pszObjectKeyName == nullptr)
     164             :                 {
     165           0 :                     continue;
     166             :                 }
     167             : 
     168         118 :                 const char *pszKeyRole = CPLGetXMLValue(psIter, "KeyRole", "");
     169         118 :                 if (pszKeyRole == nullptr)
     170             :                 {
     171           0 :                     continue;
     172             :                 }
     173         118 :                 if (EQUAL(pszKeyRole, "esriRelKeyRoleDestinationPrimary"))
     174             :                 {
     175          59 :                     aosDestinationKeys.emplace_back(pszObjectKeyName);
     176             :                 }
     177          59 :                 else if (EQUAL(pszKeyRole, "esriRelKeyRoleDestinationForeign"))
     178             :                 {
     179          59 :                     aosMappingDestinationKeys.emplace_back(pszObjectKeyName);
     180             :                 }
     181             :                 else
     182             :                 {
     183           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Unknown KeyRole: %s",
     184             :                              pszKeyRole);
     185           0 :                     return nullptr;
     186             :                 }
     187             :             }
     188             :         }
     189             :     }
     190             : 
     191         230 :     poRelationship->SetLeftTableFields(aosOriginKeys);
     192         230 :     poRelationship->SetLeftMappingTableFields(aosMappingOriginKeys);
     193         230 :     poRelationship->SetRightTableFields(aosDestinationKeys);
     194         230 :     poRelationship->SetRightMappingTableFields(aosMappingDestinationKeys);
     195             : 
     196             :     const char *pszForwardPathLabel =
     197         230 :         CPLGetXMLValue(psRelationship, "ForwardPathLabel", "");
     198         230 :     if (pszForwardPathLabel != nullptr)
     199             :     {
     200         230 :         poRelationship->SetForwardPathLabel(pszForwardPathLabel);
     201             :     }
     202             :     const char *pszBackwardPathLabel =
     203         230 :         CPLGetXMLValue(psRelationship, "BackwardPathLabel", "");
     204         230 :     if (pszBackwardPathLabel != nullptr)
     205             :     {
     206         230 :         poRelationship->SetBackwardPathLabel(pszBackwardPathLabel);
     207             :     }
     208             : 
     209             :     const char *pszIsComposite =
     210         230 :         CPLGetXMLValue(psRelationship, "IsComposite", "");
     211         230 :     if (pszIsComposite != nullptr && EQUAL(pszIsComposite, "true"))
     212             :     {
     213          91 :         poRelationship->SetType(GRT_COMPOSITE);
     214             :     }
     215             :     else
     216             :     {
     217         139 :         poRelationship->SetType(GRT_ASSOCIATION);
     218             :     }
     219             : 
     220             :     const char *pszIsAttachmentRelationship =
     221         230 :         CPLGetXMLValue(psRelationship, "IsAttachmentRelationship", "");
     222         230 :     if (pszIsAttachmentRelationship != nullptr &&
     223         230 :         EQUAL(pszIsAttachmentRelationship, "true"))
     224             :     {
     225          28 :         poRelationship->SetRelatedTableType("media");
     226             :     }
     227             :     else
     228             :     {
     229         202 :         poRelationship->SetRelatedTableType("features");
     230             :     }
     231         230 :     return poRelationship;
     232             : }
     233             : 
     234             : /************************************************************************/
     235             : /*                      BuildXMLRelationshipDef()                       */
     236             : /************************************************************************/
     237             : 
     238             : inline std::string
     239           7 : BuildXMLRelationshipDef(const GDALRelationship *poRelationship, int iDsid,
     240             :                         const std::string &osMappingTableOidName,
     241             :                         std::string &failureReason)
     242             : {
     243          14 :     std::string osNS = "typens";
     244           7 :     const char *pszRootElt = "DERelationshipClassInfo";
     245             : 
     246          14 :     CPLXMLTreeCloser oTree(CPLCreateXMLNode(nullptr, CXT_Element, pszRootElt));
     247           7 :     CPLXMLNode *psRoot = oTree.get();
     248             : 
     249           7 :     CPLAddXMLAttributeAndValue(psRoot, "xsi:type",
     250             :                                "typens:DERelationshipClassInfo");
     251             : 
     252           7 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xsi",
     253             :                                "http://www.w3.org/2001/XMLSchema-instance");
     254           7 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xs",
     255             :                                "http://www.w3.org/2001/XMLSchema");
     256           7 :     CPLAddXMLAttributeAndValue(psRoot, ("xmlns:" + osNS).c_str(),
     257             :                                "http://www.esri.com/schemas/ArcGIS/10.1");
     258             : 
     259           7 :     CPLCreateXMLElementAndValue(psRoot, "CatalogPath",
     260          14 :                                 ("\\" + poRelationship->GetName()).c_str());
     261           7 :     CPLCreateXMLElementAndValue(psRoot, "Name",
     262           7 :                                 poRelationship->GetName().c_str());
     263           7 :     CPLCreateXMLElementAndValue(psRoot, "ChildrenExpanded", "false");
     264           7 :     CPLCreateXMLElementAndValue(psRoot, "DatasetType",
     265             :                                 "esriDTRelationshipClass");
     266           7 :     CPLCreateXMLElementAndValue(psRoot, "DSID",
     267          14 :                                 CPLString().Printf("%d", iDsid));
     268           7 :     CPLCreateXMLElementAndValue(psRoot, "Versioned", "false");
     269           7 :     CPLCreateXMLElementAndValue(psRoot, "CanVersion", "false");
     270           7 :     CPLCreateXMLElementAndValue(psRoot, "ConfigurationKeyword", "");
     271           7 :     CPLCreateXMLElementAndValue(psRoot, "RequiredGeodatabaseClientVersion",
     272             :                                 "10.0");
     273           7 :     CPLCreateXMLElementAndValue(psRoot, "HasOID", "false");
     274             : 
     275             :     auto psGPFieldInfoExs =
     276           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "GPFieldInfoExs");
     277           7 :     CPLAddXMLAttributeAndValue(psGPFieldInfoExs, "xsi:type",
     278             :                                "typens:ArrayOfGPFieldInfoEx");
     279             : 
     280             :     // for many-to-many relationships this is the OID field from the mapping
     281             :     // table
     282           7 :     if (poRelationship->GetCardinality() ==
     283             :         GDALRelationshipCardinality::GRC_MANY_TO_MANY)
     284             :     {
     285           2 :         CPLCreateXMLElementAndValue(psRoot, "OIDFieldName",
     286             :                                     osMappingTableOidName.c_str());
     287             : 
     288             :         // field info from mapping table
     289             : 
     290             :         // OID field
     291             :         auto psGPFieldInfoEx =
     292           2 :             CPLCreateXMLNode(psGPFieldInfoExs, CXT_Element, "GPFieldInfoEx");
     293           2 :         CPLAddXMLAttributeAndValue(psGPFieldInfoEx, "xsi:type",
     294             :                                    "typens:GPFieldInfoEx");
     295           2 :         CPLCreateXMLElementAndValue(psGPFieldInfoEx, "Name",
     296             :                                     osMappingTableOidName.c_str());
     297             : 
     298             :         // hopefully not required...
     299             :         // CPLCreateXMLElementAndValue(psGPFieldInfoEx, "AliasName", "false");
     300             :         // CPLCreateXMLElementAndValue(psGPFieldInfoEx, "FieldType", "false");
     301             :         // CPLCreateXMLElementAndValue(psGPFieldInfoEx, "IsNullable", "false");
     302             : 
     303             :         // origin foreign key field
     304             :         psGPFieldInfoEx =
     305           2 :             CPLCreateXMLNode(psGPFieldInfoExs, CXT_Element, "GPFieldInfoEx");
     306           2 :         CPLAddXMLAttributeAndValue(psGPFieldInfoEx, "xsi:type",
     307             :                                    "typens:GPFieldInfoEx");
     308           2 :         if (!poRelationship->GetLeftMappingTableFields().empty())
     309             :         {
     310           2 :             CPLCreateXMLElementAndValue(
     311             :                 psGPFieldInfoEx, "Name",
     312           2 :                 poRelationship->GetLeftMappingTableFields()[0].c_str());
     313             :         }
     314             : 
     315             :         // destination foreign key field
     316             :         psGPFieldInfoEx =
     317           2 :             CPLCreateXMLNode(psGPFieldInfoExs, CXT_Element, "GPFieldInfoEx");
     318           2 :         CPLAddXMLAttributeAndValue(psGPFieldInfoEx, "xsi:type",
     319             :                                    "typens:GPFieldInfoEx");
     320           2 :         if (!poRelationship->GetRightMappingTableFields().empty())
     321             :         {
     322           2 :             CPLCreateXMLElementAndValue(
     323             :                 psGPFieldInfoEx, "Name",
     324           2 :                 poRelationship->GetRightMappingTableFields()[0].c_str());
     325             :         }
     326             :     }
     327             :     else
     328             :     {
     329           5 :         CPLCreateXMLElementAndValue(psRoot, "OIDFieldName", "");
     330             :     }
     331             : 
     332           7 :     CPLCreateXMLElementAndValue(psRoot, "CLSID", "");
     333           7 :     CPLCreateXMLElementAndValue(psRoot, "EXTCLSID", "");
     334             : 
     335             :     auto psRelationshipClassNames =
     336           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "RelationshipClassNames");
     337           7 :     CPLAddXMLAttributeAndValue(psRelationshipClassNames, "xsi:type",
     338             :                                "typens:Names");
     339             : 
     340           7 :     CPLCreateXMLElementAndValue(psRoot, "AliasName", "");
     341           7 :     CPLCreateXMLElementAndValue(psRoot, "ModelName", "");
     342           7 :     CPLCreateXMLElementAndValue(psRoot, "HasGlobalID", "false");
     343           7 :     CPLCreateXMLElementAndValue(psRoot, "GlobalIDFieldName", "");
     344           7 :     CPLCreateXMLElementAndValue(psRoot, "RasterFieldName", "");
     345             : 
     346             :     auto psExtensionProperties =
     347           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "ExtensionProperties");
     348           7 :     CPLAddXMLAttributeAndValue(psExtensionProperties, "xsi:type",
     349             :                                "typens:PropertySet");
     350             :     auto psPropertyArray =
     351           7 :         CPLCreateXMLNode(psExtensionProperties, CXT_Element, "PropertyArray");
     352           7 :     CPLAddXMLAttributeAndValue(psPropertyArray, "xsi:type",
     353             :                                "typens:ArrayOfPropertySetProperty");
     354             : 
     355             :     auto psControllerMemberships =
     356           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "ControllerMemberships");
     357           7 :     CPLAddXMLAttributeAndValue(psControllerMemberships, "xsi:type",
     358             :                                "typens:ArrayOfControllerMembership");
     359             : 
     360           7 :     CPLCreateXMLElementAndValue(psRoot, "EditorTrackingEnabled", "false");
     361           7 :     CPLCreateXMLElementAndValue(psRoot, "CreatorFieldName", "");
     362           7 :     CPLCreateXMLElementAndValue(psRoot, "CreatedAtFieldName", "");
     363           7 :     CPLCreateXMLElementAndValue(psRoot, "EditorFieldName", "");
     364           7 :     CPLCreateXMLElementAndValue(psRoot, "EditedAtFieldName", "");
     365           7 :     CPLCreateXMLElementAndValue(psRoot, "IsTimeInUTC", "true");
     366             : 
     367           7 :     switch (poRelationship->GetCardinality())
     368             :     {
     369           2 :         case GDALRelationshipCardinality::GRC_ONE_TO_ONE:
     370           2 :             CPLCreateXMLElementAndValue(psRoot, "Cardinality",
     371             :                                         "esriRelCardinalityOneToOne");
     372           2 :             break;
     373           3 :         case GDALRelationshipCardinality::GRC_ONE_TO_MANY:
     374           3 :             CPLCreateXMLElementAndValue(psRoot, "Cardinality",
     375             :                                         "esriRelCardinalityOneToMany");
     376           3 :             break;
     377           2 :         case GDALRelationshipCardinality::GRC_MANY_TO_MANY:
     378           2 :             CPLCreateXMLElementAndValue(psRoot, "Cardinality",
     379             :                                         "esriRelCardinalityManyToMany");
     380           2 :             break;
     381           0 :         case GDALRelationshipCardinality::GRC_MANY_TO_ONE:
     382           0 :             failureReason = "Many to one relationships are not supported";
     383           0 :             return {};
     384             :     }
     385             : 
     386           7 :     CPLCreateXMLElementAndValue(psRoot, "Notification",
     387             :                                 "esriRelNotificationNone");
     388           7 :     CPLCreateXMLElementAndValue(psRoot, "IsAttributed", "false");
     389             : 
     390           7 :     switch (poRelationship->GetType())
     391             :     {
     392           5 :         case GDALRelationshipType::GRT_ASSOCIATION:
     393           5 :             CPLCreateXMLElementAndValue(psRoot, "IsComposite", "false");
     394           5 :             break;
     395             : 
     396           2 :         case GDALRelationshipType::GRT_COMPOSITE:
     397           2 :             CPLCreateXMLElementAndValue(psRoot, "IsComposite", "true");
     398           2 :             break;
     399             : 
     400           0 :         case GDALRelationshipType::GRT_AGGREGATION:
     401           0 :             failureReason = "Aggregate relationships are not supported";
     402           0 :             return {};
     403             :     }
     404             : 
     405             :     auto psOriginClassNames =
     406           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "OriginClassNames");
     407           7 :     CPLAddXMLAttributeAndValue(psOriginClassNames, "xsi:type", "typens:Names");
     408           7 :     CPLCreateXMLElementAndValue(psOriginClassNames, "Name",
     409           7 :                                 poRelationship->GetLeftTableName().c_str());
     410             : 
     411             :     auto psDestinationClassNames =
     412           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "DestinationClassNames");
     413           7 :     CPLAddXMLAttributeAndValue(psDestinationClassNames, "xsi:type",
     414             :                                "typens:Names");
     415           7 :     CPLCreateXMLElementAndValue(psDestinationClassNames, "Name",
     416           7 :                                 poRelationship->GetRightTableName().c_str());
     417             : 
     418           7 :     CPLCreateXMLElementAndValue(psRoot, "KeyType", "esriRelKeyTypeSingle");
     419           7 :     CPLCreateXMLElementAndValue(psRoot, "ClassKey", "esriRelClassKeyUndefined");
     420           7 :     CPLCreateXMLElementAndValue(psRoot, "ForwardPathLabel",
     421           7 :                                 poRelationship->GetForwardPathLabel().c_str());
     422           7 :     CPLCreateXMLElementAndValue(psRoot, "BackwardPathLabel",
     423           7 :                                 poRelationship->GetBackwardPathLabel().c_str());
     424             : 
     425           7 :     CPLCreateXMLElementAndValue(psRoot, "IsReflexive", "false");
     426             : 
     427             :     auto psOriginClassKeys =
     428           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "OriginClassKeys");
     429           7 :     CPLAddXMLAttributeAndValue(psOriginClassKeys, "xsi:type",
     430             :                                "typens:ArrayOfRelationshipClassKey");
     431             : 
     432           7 :     auto psRelationshipClassKeyOrigin = CPLCreateXMLNode(
     433             :         psOriginClassKeys, CXT_Element, "RelationshipClassKey");
     434           7 :     CPLAddXMLAttributeAndValue(psRelationshipClassKeyOrigin, "xsi:type",
     435             :                                "typens:RelationshipClassKey");
     436           7 :     if (!poRelationship->GetLeftTableFields().empty())
     437             :     {
     438           7 :         CPLCreateXMLElementAndValue(
     439             :             psRelationshipClassKeyOrigin, "ObjectKeyName",
     440           7 :             poRelationship->GetLeftTableFields()[0].c_str());
     441             :     }
     442           7 :     CPLCreateXMLElementAndValue(psRelationshipClassKeyOrigin, "ClassKeyName",
     443             :                                 "");
     444           7 :     CPLCreateXMLElementAndValue(psRelationshipClassKeyOrigin, "KeyRole",
     445             :                                 "esriRelKeyRoleOriginPrimary");
     446             : 
     447           7 :     if (poRelationship->GetCardinality() ==
     448             :         GDALRelationshipCardinality::GRC_MANY_TO_MANY)
     449             :     {
     450           2 :         auto psRelationshipClassKeyOriginManyToMany = CPLCreateXMLNode(
     451             :             psOriginClassKeys, CXT_Element, "RelationshipClassKey");
     452           2 :         CPLAddXMLAttributeAndValue(psRelationshipClassKeyOriginManyToMany,
     453             :                                    "xsi:type", "typens:RelationshipClassKey");
     454           2 :         if (!poRelationship->GetLeftMappingTableFields().empty())
     455             :         {
     456           2 :             CPLCreateXMLElementAndValue(
     457             :                 psRelationshipClassKeyOriginManyToMany, "ObjectKeyName",
     458           2 :                 poRelationship->GetLeftMappingTableFields()[0].c_str());
     459             :         }
     460           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyOriginManyToMany,
     461             :                                     "ClassKeyName", "");
     462           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyOriginManyToMany,
     463             :                                     "KeyRole", "esriRelKeyRoleOriginForeign");
     464             :     }
     465             : 
     466           7 :     if (poRelationship->GetCardinality() !=
     467             :         GDALRelationshipCardinality::GRC_MANY_TO_MANY)
     468             :     {
     469           5 :         auto psRelationshipClassKeyForeign = CPLCreateXMLNode(
     470             :             psOriginClassKeys, CXT_Element, "RelationshipClassKey");
     471           5 :         CPLAddXMLAttributeAndValue(psRelationshipClassKeyForeign, "xsi:type",
     472             :                                    "typens:RelationshipClassKey");
     473           5 :         if (!poRelationship->GetRightTableFields().empty())
     474             :         {
     475           5 :             CPLCreateXMLElementAndValue(
     476             :                 psRelationshipClassKeyForeign, "ObjectKeyName",
     477           5 :                 poRelationship->GetRightTableFields()[0].c_str());
     478             :         }
     479           5 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeign,
     480             :                                     "ClassKeyName", "");
     481           5 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeign, "KeyRole",
     482             :                                     "esriRelKeyRoleOriginForeign");
     483             :     }
     484             :     else
     485             :     {
     486             :         auto psDestinationClassKeys =
     487           2 :             CPLCreateXMLNode(psRoot, CXT_Element, "DestinationClassKeys");
     488           2 :         CPLAddXMLAttributeAndValue(psDestinationClassKeys, "xsi:type",
     489             :                                    "typens:ArrayOfRelationshipClassKey");
     490             : 
     491           2 :         auto psRelationshipClassKeyForeign = CPLCreateXMLNode(
     492             :             psDestinationClassKeys, CXT_Element, "RelationshipClassKey");
     493           2 :         CPLAddXMLAttributeAndValue(psRelationshipClassKeyForeign, "xsi:type",
     494             :                                    "typens:RelationshipClassKey");
     495           2 :         if (!poRelationship->GetRightTableFields().empty())
     496             :         {
     497           2 :             CPLCreateXMLElementAndValue(
     498             :                 psRelationshipClassKeyForeign, "ObjectKeyName",
     499           2 :                 poRelationship->GetRightTableFields()[0].c_str());
     500             :         }
     501           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeign,
     502             :                                     "ClassKeyName", "");
     503           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeign, "KeyRole",
     504             :                                     "esriRelKeyRoleDestinationPrimary");
     505             : 
     506           2 :         auto psRelationshipClassKeyForeignManyToMany = CPLCreateXMLNode(
     507             :             psDestinationClassKeys, CXT_Element, "RelationshipClassKey");
     508           2 :         CPLAddXMLAttributeAndValue(psRelationshipClassKeyForeignManyToMany,
     509             :                                    "xsi:type", "typens:RelationshipClassKey");
     510           2 :         if (!poRelationship->GetRightMappingTableFields().empty())
     511             :         {
     512           2 :             CPLCreateXMLElementAndValue(
     513             :                 psRelationshipClassKeyForeignManyToMany, "ObjectKeyName",
     514           2 :                 poRelationship->GetRightMappingTableFields()[0].c_str());
     515             :         }
     516           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeignManyToMany,
     517             :                                     "ClassKeyName", "");
     518           2 :         CPLCreateXMLElementAndValue(psRelationshipClassKeyForeignManyToMany,
     519             :                                     "KeyRole",
     520             :                                     "esriRelKeyRoleDestinationForeign");
     521             :     }
     522             : 
     523             :     auto psRelationshipRules =
     524           7 :         CPLCreateXMLNode(psRoot, CXT_Element, "RelationshipRules");
     525           7 :     CPLAddXMLAttributeAndValue(psRelationshipRules, "xsi:type",
     526             :                                "typens:ArrayOfRelationshipRule");
     527             : 
     528           7 :     CPLCreateXMLElementAndValue(
     529             :         psRoot, "IsAttachmentRelationship",
     530           7 :         poRelationship->GetRelatedTableType() == "media" ? "true" : "false");
     531           7 :     CPLCreateXMLElementAndValue(psRoot, "ChangeTracked", "false");
     532           7 :     CPLCreateXMLElementAndValue(psRoot, "ReplicaTracked", "false");
     533             : 
     534           7 :     char *pszXML = CPLSerializeXMLTree(oTree.get());
     535          14 :     const std::string osXML(pszXML);
     536           7 :     CPLFree(pszXML);
     537           7 :     return osXML;
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                      BuildXMLRelationshipItemInfo()                  */
     542             : /************************************************************************/
     543             : 
     544             : inline std::string
     545           5 : BuildXMLRelationshipItemInfo(const GDALRelationship *poRelationship,
     546             :                              std::string & /*failureReason*/)
     547             : {
     548             :     CPLXMLTreeCloser oTree(
     549          10 :         CPLCreateXMLNode(nullptr, CXT_Element, "ESRI_ItemInformation"));
     550           5 :     CPLXMLNode *psRoot = oTree.get();
     551             : 
     552           5 :     CPLAddXMLAttributeAndValue(psRoot, "culture", "");
     553             : 
     554           5 :     CPLCreateXMLElementAndValue(psRoot, "name",
     555           5 :                                 poRelationship->GetName().c_str());
     556           5 :     CPLCreateXMLElementAndValue(psRoot, "catalogPath",
     557          10 :                                 ("\\" + poRelationship->GetName()).c_str());
     558           5 :     CPLCreateXMLElementAndValue(psRoot, "snippet", "");
     559           5 :     CPLCreateXMLElementAndValue(psRoot, "description", "");
     560           5 :     CPLCreateXMLElementAndValue(psRoot, "summary", "");
     561           5 :     CPLCreateXMLElementAndValue(psRoot, "title",
     562           5 :                                 poRelationship->GetName().c_str());
     563           5 :     CPLCreateXMLElementAndValue(psRoot, "tags", "");
     564           5 :     CPLCreateXMLElementAndValue(psRoot, "type",
     565             :                                 "File Geodatabase Relationship Class");
     566             : 
     567           5 :     auto psTypeKeywords = CPLCreateXMLNode(psRoot, CXT_Element, "typeKeywords");
     568           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword", "Data");
     569           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword", "Dataset");
     570           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword", "Vector Data");
     571           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword", "Feature Data");
     572           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword",
     573             :                                 "File Geodatabase");
     574           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword", "GDB");
     575           5 :     CPLCreateXMLElementAndValue(psTypeKeywords, "typekeyword",
     576             :                                 "Relationship Class");
     577             : 
     578           5 :     CPLCreateXMLElementAndValue(psRoot, "url", "");
     579           5 :     CPLCreateXMLElementAndValue(psRoot, "datalastModifiedTime", "");
     580             : 
     581           5 :     auto psExtent = CPLCreateXMLNode(psRoot, CXT_Element, "extent");
     582           5 :     CPLCreateXMLElementAndValue(psExtent, "xmin", "");
     583           5 :     CPLCreateXMLElementAndValue(psExtent, "ymin", "");
     584           5 :     CPLCreateXMLElementAndValue(psExtent, "xmax", "");
     585           5 :     CPLCreateXMLElementAndValue(psExtent, "ymax", "");
     586             : 
     587           5 :     CPLCreateXMLElementAndValue(psRoot, "minScale", "0");
     588           5 :     CPLCreateXMLElementAndValue(psRoot, "maxScale", "0");
     589           5 :     CPLCreateXMLElementAndValue(psRoot, "spatialReference", "");
     590           5 :     CPLCreateXMLElementAndValue(psRoot, "accessInformation", "");
     591           5 :     CPLCreateXMLElementAndValue(psRoot, "licenseInfo", "");
     592           5 :     CPLCreateXMLElementAndValue(psRoot, "typeID", "fgdb_relationship");
     593           5 :     CPLCreateXMLElementAndValue(psRoot, "isContainer", "false");
     594           5 :     CPLCreateXMLElementAndValue(psRoot, "browseDialogOnly", "false");
     595           5 :     CPLCreateXMLElementAndValue(psRoot, "propNames", "");
     596           5 :     CPLCreateXMLElementAndValue(psRoot, "propValues", "");
     597             : 
     598           5 :     char *pszXML = CPLSerializeXMLTree(oTree.get());
     599           5 :     const std::string osXML(pszXML);
     600           5 :     CPLFree(pszXML);
     601          10 :     return osXML;
     602             : }
     603             : 
     604             : /************************************************************************/
     605             : /*                 BuildXMLRelationshipDocumentation()                  */
     606             : /************************************************************************/
     607             : 
     608             : inline std::string
     609           5 : BuildXMLRelationshipDocumentation(const GDALRelationship * /*poRelationship*/,
     610             :                                   std::string & /*failureReason*/)
     611             : {
     612          10 :     CPLXMLTreeCloser oTree(CPLCreateXMLNode(nullptr, CXT_Element, "metadata"));
     613           5 :     CPLXMLNode *psRoot = oTree.get();
     614             : 
     615           5 :     CPLAddXMLAttributeAndValue(psRoot, "xml:lang", "en");
     616             : 
     617           5 :     auto psEsri = CPLCreateXMLNode(psRoot, CXT_Element, "Esri");
     618           5 :     CPLCreateXMLElementAndValue(psEsri, "CreaDate", "");
     619           5 :     CPLCreateXMLElementAndValue(psEsri, "CreaTime", "");
     620           5 :     CPLCreateXMLElementAndValue(psEsri, "ArcGISFormat", "1.0");
     621           5 :     CPLCreateXMLElementAndValue(psEsri, "SyncOnce", "TRUE");
     622             : 
     623             :     auto psDataProperties =
     624           5 :         CPLCreateXMLNode(psEsri, CXT_Element, "DataProperties");
     625           5 :     CPLCreateXMLNode(psDataProperties, CXT_Element, "lineage");
     626             : 
     627           5 :     char *pszXML = CPLSerializeXMLTree(oTree.get());
     628           5 :     const std::string osXML(pszXML);
     629           5 :     CPLFree(pszXML);
     630          10 :     return osXML;
     631             : }
     632             : 
     633             : #endif  // FILEGDB_RELATIONSHIP_H

Generated by: LCOV version 1.14