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