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
|