/src/gdal/ogr/ogrsf_frmts/gml/resolvexlinks.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GML Reader |
4 | | * Purpose: Implementation of GMLReader::ResolveXlinks() method. |
5 | | * Author: Chaitanya kumar CH, chaitanya@osgeo.in |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2010, Chaitanya kumar CH |
9 | | * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "gmlreader.h" |
16 | | #include "gmlreaderp.h" |
17 | | |
18 | | #include <cstddef> |
19 | | #include <cstring> |
20 | | |
21 | | #include "cpl_conv.h" |
22 | | #include "cpl_error.h" |
23 | | #include "cpl_http.h" |
24 | | #include "cpl_minixml.h" |
25 | | #include "cpl_string.h" |
26 | | |
27 | | /************************************************************************/ |
28 | | /* GetID() */ |
29 | | /* */ |
30 | | /* Returns the reference to the gml:id of psNode. NULL if not */ |
31 | | /* found. */ |
32 | | /************************************************************************/ |
33 | | |
34 | | static const char *GetID(CPLXMLNode *psNode) |
35 | | |
36 | 0 | { |
37 | 0 | if (psNode == nullptr) |
38 | 0 | return nullptr; |
39 | | |
40 | 0 | for (CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
41 | 0 | psChild = psChild->psNext) |
42 | 0 | { |
43 | 0 | if (psChild->eType == CXT_Attribute && |
44 | 0 | EQUAL(psChild->pszValue, "gml:id")) |
45 | 0 | { |
46 | 0 | return psChild->psChild->pszValue; |
47 | 0 | } |
48 | 0 | } |
49 | 0 | return nullptr; |
50 | 0 | } |
51 | | |
52 | | /************************************************************************/ |
53 | | /* CompareNodeIDs() */ |
54 | | /* */ |
55 | | /* Compares two nodes by their IDs */ |
56 | | /************************************************************************/ |
57 | | |
58 | | /*static int CompareNodeIDs( CPLXMLNode * psNode1, CPLXMLNode * psNode2 ) |
59 | | |
60 | | { |
61 | | if( psNode2 == NULL ) |
62 | | return TRUE; |
63 | | |
64 | | if( psNode1 == NULL ) |
65 | | return FALSE; |
66 | | |
67 | | return strcmp( GetID(psNode2), GetID(psNode1) ) > 0; |
68 | | }*/ |
69 | | |
70 | | /************************************************************************/ |
71 | | /* BuildIDIndex() */ |
72 | | /* */ |
73 | | /* Returns an array of nodes sorted by their gml:id strings */ |
74 | | /* XXX: This method can be used to build an array of pointers to */ |
75 | | /* nodes sorted by their id values. */ |
76 | | /************************************************************************/ |
77 | | /* |
78 | | static std::vector<CPLXMLNode*> BuildIDIndex( CPLXMLNode* psNode, |
79 | | std::vector<CPLXMLNode*> &apsNode ) |
80 | | |
81 | | { |
82 | | for( CPLXMLNode *psSibling = psNode; |
83 | | psSibling != NULL; |
84 | | psSibling = psSibling->psNext ) |
85 | | { |
86 | | if( GetID( psSibling ) != NULL ) |
87 | | apsNode.push_back( psSibling ); |
88 | | BuildIDIndex( psNode->psChild, apsNode ); |
89 | | } |
90 | | return NULL; |
91 | | }*/ |
92 | | |
93 | | /************************************************************************/ |
94 | | /* FindElementByID() */ |
95 | | /* */ |
96 | | /* Find a node with the indicated "gml:id" in the node tree and */ |
97 | | /* its siblings. */ |
98 | | /************************************************************************/ |
99 | | |
100 | | static CPLXMLNode *FindElementByID(CPLXMLNode *psRoot, const char *pszID) |
101 | | |
102 | 0 | { |
103 | 0 | if (psRoot == nullptr) |
104 | 0 | return nullptr; |
105 | | |
106 | | // Check for id attribute. |
107 | 0 | for (CPLXMLNode *psSibling = psRoot; psSibling != nullptr; |
108 | 0 | psSibling = psSibling->psNext) |
109 | 0 | { |
110 | 0 | if (psSibling->eType == CXT_Element) |
111 | 0 | { |
112 | | // check that sibling for id value |
113 | 0 | const char *pszIDOfSibling = GetID(psSibling); |
114 | 0 | if (pszIDOfSibling != nullptr && EQUAL(pszIDOfSibling, pszID)) |
115 | 0 | return psSibling; |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | // Search the child elements of all the psRoot's siblings. |
120 | 0 | for (CPLXMLNode *psSibling = psRoot; psSibling != nullptr; |
121 | 0 | psSibling = psSibling->psNext) |
122 | 0 | { |
123 | 0 | if (psSibling->eType == CXT_Element) |
124 | 0 | { |
125 | 0 | CPLXMLNode *psReturn = FindElementByID(psSibling->psChild, pszID); |
126 | 0 | if (psReturn != nullptr) |
127 | 0 | return psReturn; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | return nullptr; |
131 | 0 | } |
132 | | |
133 | | /************************************************************************/ |
134 | | /* RemoveIDs() */ |
135 | | /* */ |
136 | | /* Remove all the gml:id nodes. Doesn't check psRoot's siblings */ |
137 | | /************************************************************************/ |
138 | | |
139 | | static void RemoveIDs(CPLXMLNode *psRoot) |
140 | | |
141 | 0 | { |
142 | 0 | if (psRoot == nullptr) |
143 | 0 | return; |
144 | | |
145 | 0 | CPLXMLNode *psChild = psRoot->psChild; |
146 | | |
147 | | // Check for id attribute. |
148 | 0 | while (psChild != nullptr && !(psChild->eType == CXT_Attribute && |
149 | 0 | EQUAL(psChild->pszValue, "gml:id"))) |
150 | 0 | psChild = psChild->psNext; |
151 | 0 | CPLRemoveXMLChild(psRoot, psChild); |
152 | 0 | CPLDestroyXMLNode(psChild); |
153 | | |
154 | | // Search the child elements of psRoot. |
155 | 0 | for (psChild = psRoot->psChild; psChild != nullptr; |
156 | 0 | psChild = psChild->psNext) |
157 | 0 | if (psChild->eType == CXT_Element) |
158 | 0 | RemoveIDs(psChild); |
159 | 0 | } |
160 | | |
161 | | /************************************************************************/ |
162 | | /* TrimTree() */ |
163 | | /* */ |
164 | | /* Remove all nodes without a gml:id node in the descendants. */ |
165 | | /* Returns TRUE if there is a gml:id node in the descendants. */ |
166 | | /************************************************************************/ |
167 | | |
168 | | static bool TrimTree(CPLXMLNode *psRoot) |
169 | | |
170 | 0 | { |
171 | 0 | if (psRoot == nullptr) |
172 | 0 | return false; |
173 | | |
174 | 0 | CPLXMLNode *psChild = psRoot->psChild; |
175 | | |
176 | | // Check for id attribute. |
177 | 0 | while (psChild != nullptr && !(psChild->eType == CXT_Attribute && |
178 | 0 | EQUAL(psChild->pszValue, "gml:id"))) |
179 | 0 | psChild = psChild->psNext; |
180 | |
|
181 | 0 | if (psChild != nullptr) |
182 | 0 | return true; |
183 | | |
184 | | // Search the child elements of psRoot. |
185 | 0 | bool bReturn = false; |
186 | 0 | for (psChild = psRoot->psChild; psChild != nullptr;) |
187 | 0 | { |
188 | 0 | CPLXMLNode *psNextChild = psChild->psNext; |
189 | 0 | if (psChild->eType == CXT_Element) |
190 | 0 | { |
191 | 0 | const bool bRemove = TrimTree(psChild); |
192 | 0 | if (bRemove) |
193 | 0 | { |
194 | 0 | bReturn = bRemove; |
195 | 0 | } |
196 | 0 | else |
197 | 0 | { |
198 | | // Remove this child. |
199 | 0 | CPLRemoveXMLChild(psRoot, psChild); |
200 | 0 | CPLDestroyXMLNode(psChild); |
201 | 0 | } |
202 | 0 | } |
203 | |
|
204 | 0 | psChild = psNextChild; |
205 | 0 | } |
206 | 0 | return bReturn; |
207 | 0 | } |
208 | | |
209 | | /************************************************************************/ |
210 | | /* CorrectURLs() */ |
211 | | /* */ |
212 | | /* Processes the node and all its children recursively. Siblings of */ |
213 | | /* psRoot are ignored. */ |
214 | | /* - Replaces all every URL in URL#id pairs with pszURL. */ |
215 | | /* - Leaves it alone if the paths are same or the URL is not relative. */ |
216 | | /* - If it is relative, the path from pszURL is prepended. */ |
217 | | /************************************************************************/ |
218 | | |
219 | | static void CorrectURLs(CPLXMLNode *psRoot, const char *pszURL) |
220 | | |
221 | 0 | { |
222 | 0 | if (psRoot == nullptr || pszURL == nullptr) |
223 | 0 | return; |
224 | 0 | if (pszURL[0] == '\0') |
225 | 0 | return; |
226 | | |
227 | 0 | CPLXMLNode *psChild = psRoot->psChild; |
228 | | |
229 | | // Check for xlink:href attribute. |
230 | 0 | while (psChild != nullptr && !((psChild->eType == CXT_Attribute) && |
231 | 0 | (EQUAL(psChild->pszValue, "xlink:href")))) |
232 | 0 | psChild = psChild->psNext; |
233 | |
|
234 | 0 | if (psChild != nullptr && |
235 | 0 | !(strstr(psChild->psChild->pszValue, pszURL) == |
236 | 0 | psChild->psChild->pszValue && |
237 | 0 | psChild->psChild->pszValue[strlen(pszURL)] == '#')) |
238 | 0 | { |
239 | | // href has a different url. |
240 | 0 | if (psChild->psChild->pszValue[0] == '#') |
241 | 0 | { |
242 | | // Empty URL: prepend the given URL. |
243 | 0 | const size_t nLen = CPLStrnlen(pszURL, 1024) + |
244 | 0 | CPLStrnlen(psChild->psChild->pszValue, 1024) + |
245 | 0 | 1; |
246 | 0 | char *pszNew = static_cast<char *>(CPLMalloc(nLen * sizeof(char))); |
247 | 0 | CPLStrlcpy(pszNew, pszURL, nLen); |
248 | 0 | CPLStrlcat(pszNew, psChild->psChild->pszValue, nLen); |
249 | 0 | CPLSetXMLValue(psRoot, "#xlink:href", pszNew); |
250 | 0 | CPLFree(pszNew); |
251 | 0 | } |
252 | 0 | else |
253 | 0 | { |
254 | 0 | size_t nPathLen = strlen(pszURL); // Used after for. |
255 | 0 | for (; nPathLen > 0 && pszURL[nPathLen - 1] != '/' && |
256 | 0 | pszURL[nPathLen - 1] != '\\'; |
257 | 0 | nPathLen--) |
258 | 0 | { |
259 | 0 | } |
260 | |
|
261 | 0 | const char *pszDash = strchr(psChild->psChild->pszValue, '#'); |
262 | 0 | if (pszDash != nullptr && |
263 | 0 | strncmp(pszURL, psChild->psChild->pszValue, nPathLen) != 0) |
264 | 0 | { |
265 | | // Different path. |
266 | 0 | const int nURLLen = |
267 | 0 | static_cast<int>(pszDash - psChild->psChild->pszValue); |
268 | 0 | char *pszURLWithoutID = static_cast<char *>( |
269 | 0 | CPLMalloc((nURLLen + 1) * sizeof(char))); |
270 | 0 | strncpy(pszURLWithoutID, psChild->psChild->pszValue, nURLLen); |
271 | 0 | pszURLWithoutID[nURLLen] = '\0'; |
272 | |
|
273 | 0 | if (CPLIsFilenameRelative(pszURLWithoutID) && |
274 | 0 | strstr(pszURLWithoutID, ":") == nullptr) |
275 | 0 | { |
276 | | // Relative URL: prepend the path of pszURL. |
277 | 0 | const size_t nLen = |
278 | 0 | nPathLen + |
279 | 0 | CPLStrnlen(psChild->psChild->pszValue, 1024) + 1; |
280 | 0 | char *pszNew = |
281 | 0 | static_cast<char *>(CPLMalloc(nLen * sizeof(char))); |
282 | 0 | for (size_t i = 0; i < nPathLen; i++) |
283 | 0 | pszNew[i] = pszURL[i]; |
284 | 0 | pszNew[nPathLen] = '\0'; |
285 | 0 | CPLStrlcat(pszNew, psChild->psChild->pszValue, nLen); |
286 | 0 | CPLSetXMLValue(psRoot, "#xlink:href", pszNew); |
287 | 0 | CPLFree(pszNew); |
288 | 0 | } |
289 | 0 | CPLFree(pszURLWithoutID); |
290 | 0 | } |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | // Search the child elements of psRoot. |
295 | 0 | for (psChild = psRoot->psChild; psChild != nullptr; |
296 | 0 | psChild = psChild->psNext) |
297 | 0 | if (psChild->eType == CXT_Element) |
298 | 0 | CorrectURLs(psChild, pszURL); |
299 | 0 | } |
300 | | |
301 | | /************************************************************************/ |
302 | | /* FindTreeByURL() */ |
303 | | /* */ |
304 | | /* Find a doc tree that is located at pszURL. */ |
305 | | /* If not present in ppapsRoot, it updates it and ppapszResourceHREF. */ |
306 | | /************************************************************************/ |
307 | | |
308 | | static CPLXMLNode *FindTreeByURL(CPLXMLNode ***ppapsRoot, |
309 | | char ***ppapszResourceHREF, const char *pszURL) |
310 | | |
311 | 0 | { |
312 | 0 | if (*ppapsRoot == nullptr || ppapszResourceHREF == nullptr) |
313 | 0 | return nullptr; |
314 | | |
315 | | // If found in ppapszResourceHREF. |
316 | 0 | const int i = CSLFindString(*ppapszResourceHREF, pszURL); |
317 | 0 | if (i >= 0) |
318 | 0 | { |
319 | | // Return corresponding psRoot. |
320 | 0 | return (*ppapsRoot)[i]; |
321 | 0 | } |
322 | | |
323 | 0 | CPLXMLNode *psSrcTree = nullptr; |
324 | 0 | char *pszLocation = CPLStrdup(pszURL); |
325 | | // If it is part of filesystem. |
326 | 0 | if (CPLCheckForFile(pszLocation, nullptr)) |
327 | 0 | { |
328 | | // Filesystem. |
329 | 0 | psSrcTree = CPLParseXMLFile(pszURL); |
330 | 0 | } |
331 | 0 | else if (CPLHTTPEnabled()) |
332 | 0 | { |
333 | | // Web resource. |
334 | 0 | CPLErrorReset(); |
335 | 0 | CPLHTTPResult *psResult = CPLHTTPFetch(pszURL, nullptr); |
336 | 0 | if (psResult != nullptr) |
337 | 0 | { |
338 | 0 | if (psResult->nDataLen > 0 && CPLGetLastErrorNo() == 0) |
339 | 0 | psSrcTree = CPLParseXMLString( |
340 | 0 | reinterpret_cast<const char *>(psResult->pabyData)); |
341 | 0 | CPLHTTPDestroyResult(psResult); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | // Report error in case the resource cannot be retrieved. |
346 | 0 | if (psSrcTree == nullptr) |
347 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Could not access %s", |
348 | 0 | pszLocation); |
349 | |
|
350 | 0 | CPLFree(pszLocation); |
351 | | |
352 | | /************************************************************************/ |
353 | | /* In the external GML resource we will only need elements */ |
354 | | /* identified by a "gml:id". So trim them. */ |
355 | | /************************************************************************/ |
356 | 0 | CPLXMLNode *psSibling = psSrcTree; |
357 | 0 | while (psSibling != nullptr) |
358 | 0 | { |
359 | 0 | TrimTree(psSibling); |
360 | 0 | psSibling = psSibling->psNext; |
361 | 0 | } |
362 | | |
363 | | // Update to lists. |
364 | 0 | int nItems = CSLCount(*ppapszResourceHREF); |
365 | 0 | *ppapszResourceHREF = CSLAddString(*ppapszResourceHREF, pszURL); |
366 | 0 | *ppapsRoot = static_cast<CPLXMLNode **>( |
367 | 0 | CPLRealloc(*ppapsRoot, (nItems + 2) * sizeof(CPLXMLNode *))); |
368 | 0 | (*ppapsRoot)[nItems] = psSrcTree; |
369 | 0 | (*ppapsRoot)[nItems + 1] = nullptr; |
370 | | |
371 | | // Return the tree. |
372 | 0 | return (*ppapsRoot)[nItems]; |
373 | 0 | } |
374 | | |
375 | | /************************************************************************/ |
376 | | /* ResolveTree() */ |
377 | | /* Resolves the xlinks in a node and its siblings */ |
378 | | /* If any error is encountered or any element is skipped(papszSkip): */ |
379 | | /* If bStrict is TRUE, process is stopped and CE_Error is returned */ |
380 | | /* If bStrict is FALSE, the process is continued but CE_Warning is */ |
381 | | /* returned at the end. */ |
382 | | /* If everything goes fine, CE_None is returned. */ |
383 | | /************************************************************************/ |
384 | | |
385 | | static CPLErr Resolve(CPLXMLNode *psNode, CPLXMLNode ***ppapsRoot, |
386 | | char ***ppapszResourceHREF, char **papszSkip, |
387 | | const int bStrict, int nDepth) |
388 | | |
389 | 0 | { |
390 | | // For each sibling. |
391 | 0 | CPLXMLNode *psSibling = nullptr; |
392 | 0 | CPLXMLNode *psResource = nullptr; |
393 | 0 | CPLXMLNode *psTarget = nullptr; |
394 | 0 | CPLErr eReturn = CE_None, eReturned; |
395 | |
|
396 | 0 | for (psSibling = psNode; psSibling != nullptr; |
397 | 0 | psSibling = psSibling->psNext) |
398 | 0 | { |
399 | 0 | if (psSibling->eType != CXT_Element) |
400 | 0 | continue; |
401 | | |
402 | 0 | CPLXMLNode *psChild = psSibling->psChild; |
403 | 0 | while (psChild != nullptr && !(psChild->eType == CXT_Attribute && |
404 | 0 | EQUAL(psChild->pszValue, "xlink:href"))) |
405 | 0 | psChild = psChild->psNext; |
406 | | |
407 | | // If a child has a "xlink:href" attribute. |
408 | 0 | if (psChild != nullptr && psChild->psChild != nullptr) |
409 | 0 | { |
410 | 0 | if (CSLFindString(papszSkip, psSibling->pszValue) >= 0) |
411 | 0 | { |
412 | | // Skipping a specified element. |
413 | 0 | eReturn = CE_Warning; |
414 | 0 | continue; |
415 | 0 | } |
416 | | |
417 | 0 | const int nDepthCheck = 256; |
418 | 0 | if (nDepth % nDepthCheck == 0) |
419 | 0 | { |
420 | | // A way to track progress. |
421 | 0 | CPLDebug("GML", "Resolving xlinks... (currently %s)", |
422 | 0 | psChild->psChild->pszValue); |
423 | 0 | } |
424 | |
|
425 | 0 | char **papszTokens = CSLTokenizeString2( |
426 | 0 | psChild->psChild->pszValue, "#", |
427 | 0 | CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | |
428 | 0 | CSLT_STRIPENDSPACES); |
429 | 0 | if (CSLCount(papszTokens) != 2 || papszTokens[1][0] == '\0') |
430 | 0 | { |
431 | 0 | CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported, |
432 | 0 | "Error parsing the href %s.%s", |
433 | 0 | psChild->psChild->pszValue, |
434 | 0 | bStrict ? "" : " Skipping..."); |
435 | 0 | CSLDestroy(papszTokens); |
436 | 0 | if (bStrict) |
437 | 0 | return CE_Failure; |
438 | 0 | eReturn = CE_Warning; |
439 | 0 | continue; |
440 | 0 | } |
441 | | |
442 | | // Look for the resource with that URL. |
443 | 0 | psResource = |
444 | 0 | FindTreeByURL(ppapsRoot, ppapszResourceHREF, papszTokens[0]); |
445 | 0 | if (psResource == nullptr) |
446 | 0 | { |
447 | 0 | CSLDestroy(papszTokens); |
448 | 0 | if (bStrict) |
449 | 0 | return CE_Failure; |
450 | 0 | eReturn = CE_Warning; |
451 | 0 | continue; |
452 | 0 | } |
453 | | |
454 | | // Look for the element with the ID. |
455 | 0 | psTarget = FindElementByID(psResource, papszTokens[1]); |
456 | 0 | if (psTarget != nullptr) |
457 | 0 | { |
458 | | // Remove the xlink:href attribute. |
459 | 0 | CPLRemoveXMLChild(psSibling, psChild); |
460 | 0 | CPLDestroyXMLNode(psChild); |
461 | | |
462 | | // Make a copy of psTarget. |
463 | 0 | CPLXMLNode *psCopy = |
464 | 0 | CPLCreateXMLNode(nullptr, CXT_Element, psTarget->pszValue); |
465 | 0 | psCopy->psChild = CPLCloneXMLTree(psTarget->psChild); |
466 | 0 | RemoveIDs(psCopy); |
467 | | // Correct empty URLs in URL#id pairs. |
468 | 0 | if (CPLStrnlen(papszTokens[0], 1) > 0) |
469 | 0 | { |
470 | 0 | CorrectURLs(psCopy, papszTokens[0]); |
471 | 0 | } |
472 | 0 | CPLAddXMLChild(psSibling, psCopy); |
473 | 0 | CSLDestroy(papszTokens); |
474 | 0 | } |
475 | 0 | else |
476 | 0 | { |
477 | | // Element not found. |
478 | 0 | CSLDestroy(papszTokens); |
479 | 0 | CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_ObjectNull, |
480 | 0 | "Couldn't find the element with id %s.", |
481 | 0 | psChild->psChild->pszValue); |
482 | 0 | if (bStrict) |
483 | 0 | return CE_Failure; |
484 | 0 | eReturn = CE_Warning; |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | // Recurse with the first child. |
489 | 0 | eReturned = Resolve(psSibling->psChild, ppapsRoot, ppapszResourceHREF, |
490 | 0 | papszSkip, bStrict, nDepth + 1); |
491 | |
|
492 | 0 | if (eReturned == CE_Failure) |
493 | 0 | return CE_Failure; |
494 | | |
495 | 0 | if (eReturned == CE_Warning) |
496 | 0 | eReturn = CE_Warning; |
497 | 0 | } |
498 | 0 | return eReturn; |
499 | 0 | } |
500 | | |
501 | | /************************************************************************/ |
502 | | /* ResolveXlinks() */ |
503 | | /* Returns TRUE for success */ |
504 | | /* - Returns CE_None for success, */ |
505 | | /* CE_Warning if the resolved file is saved to a different file or */ |
506 | | /* CE_Failure if it could not be saved at all. */ |
507 | | /* - m_pszFilename will be set to the file the resolved file was */ |
508 | | /* saved to. */ |
509 | | /************************************************************************/ |
510 | | |
511 | | bool GMLReader::ResolveXlinks(const char *pszFile, bool *pbOutIsTempFile, |
512 | | char **papszSkip, const bool bStrict) |
513 | | |
514 | 0 | { |
515 | 0 | *pbOutIsTempFile = false; |
516 | | |
517 | | // Check if the original source file is set. |
518 | 0 | if (m_pszFilename == nullptr) |
519 | 0 | { |
520 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
521 | 0 | "GML source file needs to be set first with " |
522 | 0 | "GMLReader::SetSourceFile()."); |
523 | 0 | return false; |
524 | 0 | } |
525 | | |
526 | | /* -------------------------------------------------------------------- */ |
527 | | /* Load the raw XML file into a XML Node tree. */ |
528 | | /* -------------------------------------------------------------------- */ |
529 | 0 | CPLXMLNode **papsSrcTree = |
530 | 0 | static_cast<CPLXMLNode **>(CPLCalloc(2, sizeof(CPLXMLNode *))); |
531 | 0 | papsSrcTree[0] = CPLParseXMLFile(m_pszFilename); |
532 | |
|
533 | 0 | if (papsSrcTree[0] == nullptr) |
534 | 0 | { |
535 | 0 | CPLFree(papsSrcTree); |
536 | 0 | return false; |
537 | 0 | } |
538 | | |
539 | | // Make all the URLs absolute. |
540 | 0 | CPLXMLNode *psSibling = nullptr; |
541 | 0 | for (psSibling = papsSrcTree[0]; psSibling != nullptr; |
542 | 0 | psSibling = psSibling->psNext) |
543 | 0 | CorrectURLs(psSibling, m_pszFilename); |
544 | | |
545 | | // Setup resource data structure. |
546 | 0 | char **papszResourceHREF = nullptr; |
547 | | // "" is the href of the original source file. |
548 | 0 | papszResourceHREF = CSLAddString(papszResourceHREF, m_pszFilename); |
549 | | |
550 | | // Call resolver. |
551 | 0 | const CPLErr eReturned = Resolve(papsSrcTree[0], &papsSrcTree, |
552 | 0 | &papszResourceHREF, papszSkip, bStrict, 0); |
553 | |
|
554 | 0 | bool bReturn = true; |
555 | 0 | if (eReturned != CE_Failure) |
556 | 0 | { |
557 | 0 | char *pszTmpName = nullptr; |
558 | 0 | bool bTryWithTempFile = false; |
559 | 0 | if (STARTS_WITH_CI(pszFile, "/vsitar/") || |
560 | 0 | STARTS_WITH_CI(pszFile, "/vsigzip/") || |
561 | 0 | STARTS_WITH_CI(pszFile, "/vsizip/") || |
562 | 0 | STARTS_WITH_CI(pszFile, "/vsicurl")) |
563 | 0 | { |
564 | 0 | bTryWithTempFile = true; |
565 | 0 | } |
566 | 0 | else if (!CPLSerializeXMLTreeToFile(papsSrcTree[0], pszFile)) |
567 | 0 | { |
568 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
569 | 0 | "Cannot serialize resolved file %s to %s.", m_pszFilename, |
570 | 0 | pszFile); |
571 | 0 | bTryWithTempFile = true; |
572 | 0 | } |
573 | |
|
574 | 0 | if (bTryWithTempFile) |
575 | 0 | { |
576 | 0 | pszTmpName = |
577 | 0 | CPLStrdup(CPLGenerateTempFilenameSafe("ResolvedGML").c_str()); |
578 | 0 | if (!CPLSerializeXMLTreeToFile(papsSrcTree[0], pszTmpName)) |
579 | 0 | { |
580 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
581 | 0 | "Cannot serialize resolved file %s to %s either.", |
582 | 0 | m_pszFilename, pszTmpName); |
583 | 0 | CPLFree(pszTmpName); |
584 | 0 | bReturn = false; |
585 | 0 | } |
586 | 0 | else |
587 | 0 | { |
588 | | // Set the source file to the resolved file. |
589 | 0 | CPLFree(m_pszFilename); |
590 | 0 | m_pszFilename = pszTmpName; |
591 | 0 | *pbOutIsTempFile = true; |
592 | 0 | } |
593 | 0 | } |
594 | 0 | else |
595 | 0 | { |
596 | | // Set the source file to the resolved file. |
597 | 0 | CPLFree(m_pszFilename); |
598 | 0 | m_pszFilename = CPLStrdup(pszFile); |
599 | 0 | } |
600 | 0 | } |
601 | 0 | else |
602 | 0 | { |
603 | 0 | bReturn = false; |
604 | 0 | } |
605 | |
|
606 | 0 | const int nItems = CSLCount(papszResourceHREF); |
607 | 0 | CSLDestroy(papszResourceHREF); |
608 | 0 | for (int i = 0; i < nItems; i++) |
609 | 0 | CPLDestroyXMLNode(papsSrcTree[i]); |
610 | 0 | CPLFree(papsSrcTree); |
611 | |
|
612 | 0 | return bReturn; |
613 | 0 | } |