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

Generated by: LCOV version 1.14