LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - resolvexlinks.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 132 247 53.4 %
Date: 2024-11-21 22:18:42 Functions: 7 8 87.5 %

          Line data    Source code
       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      107255 : static const char *GetID(CPLXMLNode *psNode)
      35             : 
      36             : {
      37      107255 :     if (psNode == nullptr)
      38           0 :         return nullptr;
      39             : 
      40      261530 :     for (CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
      41      154275 :          psChild = psChild->psNext)
      42             :     {
      43      165003 :         if (psChild->eType == CXT_Attribute &&
      44       35151 :             EQUAL(psChild->pszValue, "gml:id"))
      45             :         {
      46       10728 :             return psChild->psChild->pszValue;
      47             :         }
      48             :     }
      49       96527 :     return nullptr;
      50             : }
      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      100770 : static CPLXMLNode *FindElementByID(CPLXMLNode *psRoot, const char *pszID)
     101             : 
     102             : {
     103      100770 :     if (psRoot == nullptr)
     104        9109 :         return nullptr;
     105             : 
     106             :     // Check for id attribute.
     107      253761 :     for (CPLXMLNode *psSibling = psRoot; psSibling != nullptr;
     108      162100 :          psSibling = psSibling->psNext)
     109             :     {
     110      162413 :         if (psSibling->eType == CXT_Element)
     111             :         {
     112             :             // check that sibling for id value
     113      107255 :             const char *pszIDOfSibling = GetID(psSibling);
     114      107255 :             if (pszIDOfSibling != nullptr && EQUAL(pszIDOfSibling, pszID))
     115         313 :                 return psSibling;
     116             :         }
     117             :     }
     118             : 
     119             :     // Search the child elements of all the psRoot's siblings.
     120      244818 :     for (CPLXMLNode *psSibling = psRoot; psSibling != nullptr;
     121      153470 :          psSibling = psSibling->psNext)
     122             :     {
     123      155557 :         if (psSibling->eType == CXT_Element)
     124             :         {
     125      100457 :             CPLXMLNode *psReturn = FindElementByID(psSibling->psChild, pszID);
     126      100457 :             if (psReturn != nullptr)
     127        2087 :                 return psReturn;
     128             :         }
     129             :     }
     130       89261 :     return nullptr;
     131             : }
     132             : 
     133             : /************************************************************************/
     134             : /*                          RemoveIDs()                                 */
     135             : /*                                                                      */
     136             : /*      Remove all the gml:id nodes. Doesn't check psRoot's siblings    */
     137             : /************************************************************************/
     138             : 
     139        1690 : static void RemoveIDs(CPLXMLNode *psRoot)
     140             : 
     141             : {
     142        1690 :     if (psRoot == nullptr)
     143           0 :         return;
     144             : 
     145        1690 :     CPLXMLNode *psChild = psRoot->psChild;
     146             : 
     147             :     // Check for id attribute.
     148        3244 :     while (psChild != nullptr && !(psChild->eType == CXT_Attribute &&
     149         987 :                                    EQUAL(psChild->pszValue, "gml:id")))
     150        1554 :         psChild = psChild->psNext;
     151        1690 :     CPLRemoveXMLChild(psRoot, psChild);
     152        1690 :     CPLDestroyXMLNode(psChild);
     153             : 
     154             :     // Search the child elements of psRoot.
     155        3775 :     for (psChild = psRoot->psChild; psChild != nullptr;
     156        2085 :          psChild = psChild->psNext)
     157        2085 :         if (psChild->eType == CXT_Element)
     158        1377 :             RemoveIDs(psChild);
     159             : }
     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           0 : static bool TrimTree(CPLXMLNode *psRoot)
     169             : 
     170             : {
     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             :     {
     188           0 :         CPLXMLNode *psNextChild = psChild->psNext;
     189           0 :         if (psChild->eType == CXT_Element)
     190             :         {
     191           0 :             const bool bRemove = TrimTree(psChild);
     192           0 :             if (bRemove)
     193             :             {
     194           0 :                 bReturn = bRemove;
     195             :             }
     196             :             else
     197             :             {
     198             :                 // Remove this child.
     199           0 :                 CPLRemoveXMLChild(psRoot, psChild);
     200           0 :                 CPLDestroyXMLNode(psChild);
     201             :             }
     202             :         }
     203             : 
     204           0 :         psChild = psNextChild;
     205             :     }
     206           0 :     return bReturn;
     207             : }
     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        3490 : static void CorrectURLs(CPLXMLNode *psRoot, const char *pszURL)
     220             : 
     221             : {
     222        3490 :     if (psRoot == nullptr || pszURL == nullptr)
     223           0 :         return;
     224        3490 :     if (pszURL[0] == '\0')
     225           0 :         return;
     226             : 
     227        3490 :     CPLXMLNode *psChild = psRoot->psChild;
     228             : 
     229             :     // Check for xlink:href attribute.
     230        8449 :     while (psChild != nullptr && !((psChild->eType == CXT_Attribute) &&
     231        1552 :                                    (EQUAL(psChild->pszValue, "xlink:href"))))
     232        4959 :         psChild = psChild->psNext;
     233             : 
     234        3490 :     if (psChild != nullptr &&
     235         313 :         !(strstr(psChild->psChild->pszValue, pszURL) ==
     236         313 :               psChild->psChild->pszValue &&
     237           0 :           psChild->psChild->pszValue[strlen(pszURL)] == '#'))
     238             :     {
     239             :         // href has a different url.
     240         313 :         if (psChild->psChild->pszValue[0] == '#')
     241             :         {
     242             :             // Empty URL: prepend the given URL.
     243         313 :             const size_t nLen = CPLStrnlen(pszURL, 1024) +
     244         313 :                                 CPLStrnlen(psChild->psChild->pszValue, 1024) +
     245         313 :                                 1;
     246         313 :             char *pszNew = static_cast<char *>(CPLMalloc(nLen * sizeof(char)));
     247         313 :             CPLStrlcpy(pszNew, pszURL, nLen);
     248         313 :             CPLStrlcat(pszNew, psChild->psChild->pszValue, nLen);
     249         313 :             CPLSetXMLValue(psRoot, "#xlink:href", pszNew);
     250         313 :             CPLFree(pszNew);
     251             :         }
     252             :         else
     253             :         {
     254           0 :             size_t nPathLen = strlen(pszURL);  // Used after for.
     255           0 :             for (; nPathLen > 0 && pszURL[nPathLen - 1] != '/' &&
     256           0 :                    pszURL[nPathLen - 1] != '\\';
     257             :                  nPathLen--)
     258             :             {
     259             :             }
     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             :             {
     265             :                 // Different path.
     266           0 :                 const int nURLLen =
     267           0 :                     static_cast<int>(pszDash - psChild->psChild->pszValue);
     268             :                 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             :                 {
     276             :                     // Relative URL: prepend the path of pszURL.
     277             :                     const size_t nLen =
     278           0 :                         nPathLen +
     279           0 :                         CPLStrnlen(psChild->psChild->pszValue, 1024) + 1;
     280             :                     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             :                 }
     289           0 :                 CPLFree(pszURLWithoutID);
     290             :             }
     291             :         }
     292             :     }
     293             : 
     294             :     // Search the child elements of psRoot.
     295        8762 :     for (psChild = psRoot->psChild; psChild != nullptr;
     296        5272 :          psChild = psChild->psNext)
     297        5272 :         if (psChild->eType == CXT_Element)
     298        3171 :             CorrectURLs(psChild, pszURL);
     299             : }
     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         313 : static CPLXMLNode *FindTreeByURL(CPLXMLNode ***ppapsRoot,
     309             :                                  char ***ppapszResourceHREF, const char *pszURL)
     310             : 
     311             : {
     312         313 :     if (*ppapsRoot == nullptr || ppapszResourceHREF == nullptr)
     313           0 :         return nullptr;
     314             : 
     315             :     // If found in ppapszResourceHREF.
     316         313 :     const int i = CSLFindString(*ppapszResourceHREF, pszURL);
     317         313 :     if (i >= 0)
     318             :     {
     319             :         // Return corresponding psRoot.
     320         313 :         return (*ppapsRoot)[i];
     321             :     }
     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             :     {
     328             :         // Filesystem.
     329           0 :         psSrcTree = CPLParseXMLFile(pszURL);
     330             :     }
     331           0 :     else if (CPLHTTPEnabled())
     332             :     {
     333             :         // Web resource.
     334           0 :         CPLErrorReset();
     335           0 :         CPLHTTPResult *psResult = CPLHTTPFetch(pszURL, nullptr);
     336           0 :         if (psResult != nullptr)
     337             :         {
     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             :         }
     343             :     }
     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             :                  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             :     {
     359           0 :         TrimTree(psSibling);
     360           0 :         psSibling = psSibling->psNext;
     361             :     }
     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             : }
     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        3493 : static CPLErr Resolve(CPLXMLNode *psNode, CPLXMLNode ***ppapsRoot,
     386             :                       char ***ppapszResourceHREF, char **papszSkip,
     387             :                       const int bStrict, int nDepth)
     388             : 
     389             : {
     390             :     // For each sibling.
     391        3493 :     CPLXMLNode *psSibling = nullptr;
     392        3493 :     CPLXMLNode *psResource = nullptr;
     393        3493 :     CPLXMLNode *psTarget = nullptr;
     394        3493 :     CPLErr eReturn = CE_None, eReturned;
     395             : 
     396        8771 :     for (psSibling = psNode; psSibling != nullptr;
     397        5278 :          psSibling = psSibling->psNext)
     398             :     {
     399        5278 :         if (psSibling->eType != CXT_Element)
     400        1788 :             continue;
     401             : 
     402        3490 :         CPLXMLNode *psChild = psSibling->psChild;
     403        8449 :         while (psChild != nullptr && !(psChild->eType == CXT_Attribute &&
     404        1552 :                                        EQUAL(psChild->pszValue, "xlink:href")))
     405        4959 :             psChild = psChild->psNext;
     406             : 
     407             :         // If a child has a "xlink:href" attribute.
     408        3490 :         if (psChild != nullptr && psChild->psChild != nullptr)
     409             :         {
     410         313 :             if (CSLFindString(papszSkip, psSibling->pszValue) >= 0)
     411             :             {
     412             :                 // Skipping a specified element.
     413           0 :                 eReturn = CE_Warning;
     414           0 :                 continue;
     415             :             }
     416             : 
     417         313 :             const int nDepthCheck = 256;
     418         313 :             if (nDepth % nDepthCheck == 0)
     419             :             {
     420             :                 // A way to track progress.
     421           0 :                 CPLDebug("GML", "Resolving xlinks... (currently %s)",
     422           0 :                          psChild->psChild->pszValue);
     423             :             }
     424             : 
     425         626 :             char **papszTokens = CSLTokenizeString2(
     426         313 :                 psChild->psChild->pszValue, "#",
     427             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
     428             :                     CSLT_STRIPENDSPACES);
     429         313 :             if (CSLCount(papszTokens) != 2 || papszTokens[1][0] == '\0')
     430             :             {
     431           0 :                 CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
     432             :                          "Error parsing the href %s.%s",
     433           0 :                          psChild->psChild->pszValue,
     434             :                          bStrict ? "" : " Skipping...");
     435           0 :                 CSLDestroy(papszTokens);
     436           0 :                 if (bStrict)
     437           0 :                     return CE_Failure;
     438           0 :                 eReturn = CE_Warning;
     439           0 :                 continue;
     440             :             }
     441             : 
     442             :             // Look for the resource with that URL.
     443             :             psResource =
     444         313 :                 FindTreeByURL(ppapsRoot, ppapszResourceHREF, papszTokens[0]);
     445         313 :             if (psResource == nullptr)
     446             :             {
     447           0 :                 CSLDestroy(papszTokens);
     448           0 :                 if (bStrict)
     449           0 :                     return CE_Failure;
     450           0 :                 eReturn = CE_Warning;
     451           0 :                 continue;
     452             :             }
     453             : 
     454             :             // Look for the element with the ID.
     455         313 :             psTarget = FindElementByID(psResource, papszTokens[1]);
     456         313 :             if (psTarget != nullptr)
     457             :             {
     458             :                 // Remove the xlink:href attribute.
     459         313 :                 CPLRemoveXMLChild(psSibling, psChild);
     460         313 :                 CPLDestroyXMLNode(psChild);
     461             : 
     462             :                 // Make a copy of psTarget.
     463             :                 CPLXMLNode *psCopy =
     464         313 :                     CPLCreateXMLNode(nullptr, CXT_Element, psTarget->pszValue);
     465         313 :                 psCopy->psChild = CPLCloneXMLTree(psTarget->psChild);
     466         313 :                 RemoveIDs(psCopy);
     467             :                 // Correct empty URLs in URL#id pairs.
     468         313 :                 if (CPLStrnlen(papszTokens[0], 1) > 0)
     469             :                 {
     470         313 :                     CorrectURLs(psCopy, papszTokens[0]);
     471             :                 }
     472         313 :                 CPLAddXMLChild(psSibling, psCopy);
     473         313 :                 CSLDestroy(papszTokens);
     474             :             }
     475             :             else
     476             :             {
     477             :                 // Element not found.
     478           0 :                 CSLDestroy(papszTokens);
     479           0 :                 CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_ObjectNull,
     480             :                          "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             :             }
     486             :         }
     487             : 
     488             :         // Recurse with the first child.
     489        3490 :         eReturned = Resolve(psSibling->psChild, ppapsRoot, ppapszResourceHREF,
     490             :                             papszSkip, bStrict, nDepth + 1);
     491             : 
     492        3490 :         if (eReturned == CE_Failure)
     493           0 :             return CE_Failure;
     494             : 
     495        3490 :         if (eReturned == CE_Warning)
     496           0 :             eReturn = CE_Warning;
     497             :     }
     498        3493 :     return eReturn;
     499             : }
     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           3 : bool GMLReader::ResolveXlinks(const char *pszFile, bool *pbOutIsTempFile,
     512             :                               char **papszSkip, const bool bStrict)
     513             : 
     514             : {
     515           3 :     *pbOutIsTempFile = false;
     516             : 
     517             :     // Check if the original source file is set.
     518           3 :     if (m_pszFilename == nullptr)
     519             :     {
     520           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     521             :                  "GML source file needs to be set first with "
     522             :                  "GMLReader::SetSourceFile().");
     523           0 :         return false;
     524             :     }
     525             : 
     526             :     /* -------------------------------------------------------------------- */
     527             :     /*      Load the raw XML file into a XML Node tree.                     */
     528             :     /* -------------------------------------------------------------------- */
     529             :     CPLXMLNode **papsSrcTree =
     530           3 :         static_cast<CPLXMLNode **>(CPLCalloc(2, sizeof(CPLXMLNode *)));
     531           3 :     papsSrcTree[0] = CPLParseXMLFile(m_pszFilename);
     532             : 
     533           3 :     if (papsSrcTree[0] == nullptr)
     534             :     {
     535           0 :         CPLFree(papsSrcTree);
     536           0 :         return false;
     537             :     }
     538             : 
     539             :     // Make all the URLs absolute.
     540           3 :     CPLXMLNode *psSibling = nullptr;
     541           9 :     for (psSibling = papsSrcTree[0]; psSibling != nullptr;
     542           6 :          psSibling = psSibling->psNext)
     543           6 :         CorrectURLs(psSibling, m_pszFilename);
     544             : 
     545             :     // Setup resource data structure.
     546           3 :     char **papszResourceHREF = nullptr;
     547             :     // "" is the href of the original source file.
     548           3 :     papszResourceHREF = CSLAddString(papszResourceHREF, m_pszFilename);
     549             : 
     550             :     // Call resolver.
     551           3 :     const CPLErr eReturned = Resolve(papsSrcTree[0], &papsSrcTree,
     552             :                                      &papszResourceHREF, papszSkip, bStrict, 0);
     553             : 
     554           3 :     bool bReturn = true;
     555           3 :     if (eReturned != CE_Failure)
     556             :     {
     557           3 :         char *pszTmpName = nullptr;
     558           3 :         bool bTryWithTempFile = false;
     559           3 :         if (STARTS_WITH_CI(pszFile, "/vsitar/") ||
     560           3 :             STARTS_WITH_CI(pszFile, "/vsigzip/") ||
     561           3 :             STARTS_WITH_CI(pszFile, "/vsizip/") ||
     562           3 :             STARTS_WITH_CI(pszFile, "/vsicurl"))
     563             :         {
     564           0 :             bTryWithTempFile = true;
     565             :         }
     566           3 :         else if (!CPLSerializeXMLTreeToFile(papsSrcTree[0], pszFile))
     567             :         {
     568           0 :             CPLError(CE_Failure, CPLE_FileIO,
     569             :                      "Cannot serialize resolved file %s to %s.", m_pszFilename,
     570             :                      pszFile);
     571           0 :             bTryWithTempFile = true;
     572             :         }
     573             : 
     574           3 :         if (bTryWithTempFile)
     575             :         {
     576           0 :             pszTmpName = CPLStrdup(CPLGenerateTempFilename("ResolvedGML"));
     577           0 :             if (!CPLSerializeXMLTreeToFile(papsSrcTree[0], pszTmpName))
     578             :             {
     579           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     580             :                          "Cannot serialize resolved file %s to %s either.",
     581             :                          m_pszFilename, pszTmpName);
     582           0 :                 CPLFree(pszTmpName);
     583           0 :                 bReturn = false;
     584             :             }
     585             :             else
     586             :             {
     587             :                 // Set the source file to the resolved file.
     588           0 :                 CPLFree(m_pszFilename);
     589           0 :                 m_pszFilename = pszTmpName;
     590           0 :                 *pbOutIsTempFile = true;
     591             :             }
     592             :         }
     593             :         else
     594             :         {
     595             :             // Set the source file to the resolved file.
     596           3 :             CPLFree(m_pszFilename);
     597           3 :             m_pszFilename = CPLStrdup(pszFile);
     598             :         }
     599             :     }
     600             :     else
     601             :     {
     602           0 :         bReturn = false;
     603             :     }
     604             : 
     605           3 :     const int nItems = CSLCount(papszResourceHREF);
     606           3 :     CSLDestroy(papszResourceHREF);
     607           6 :     for (int i = 0; i < nItems; i++)
     608           3 :         CPLDestroyXMLNode(papsSrcTree[i]);
     609           3 :     CPLFree(papsSrcTree);
     610             : 
     611           3 :     return bReturn;
     612             : }

Generated by: LCOV version 1.14