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