LCOV - code coverage report
Current view: top level - gcore - gdalmultidomainmetadata.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 115 120 95.8 %
Date: 2024-05-04 12:52:34 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Implementation of GDALMultiDomainMetadata class.  This class
       5             :  *           manages metadata items for a variable list of domains.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : #include "gdal_priv.h"
      33             : 
      34             : #include <cstring>
      35             : 
      36             : #include "cpl_conv.h"
      37             : #include "cpl_error.h"
      38             : #include "cpl_minixml.h"
      39             : #include "cpl_string.h"
      40             : #include "gdal_pam.h"
      41             : 
      42             : //! @cond Doxygen_Suppress
      43             : /************************************************************************/
      44             : /*                      GDALMultiDomainMetadata()                       */
      45             : /************************************************************************/
      46             : 
      47             : GDALMultiDomainMetadata::GDALMultiDomainMetadata() = default;
      48             : 
      49             : /************************************************************************/
      50             : /*                      ~GDALMultiDomainMetadata()                      */
      51             : /************************************************************************/
      52             : 
      53             : GDALMultiDomainMetadata::~GDALMultiDomainMetadata() = default;
      54             : 
      55             : /************************************************************************/
      56             : /*                               Clear()                                */
      57             : /************************************************************************/
      58             : 
      59           0 : void GDALMultiDomainMetadata::Clear()
      60             : 
      61             : {
      62           0 :     aosDomainList.clear();
      63           0 :     oMetadata.clear();
      64           0 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                           SanitizeDomain()                           */
      68             : /************************************************************************/
      69             : 
      70    13964300 : static inline const char *SanitizeDomain(const char *pszDomain)
      71             : {
      72    13964300 :     return pszDomain ? pszDomain : "";
      73             : }
      74             : 
      75             : /************************************************************************/
      76             : /*                            GetMetadata()                             */
      77             : /************************************************************************/
      78             : 
      79      153693 : char **GDALMultiDomainMetadata::GetMetadata(const char *pszDomain)
      80             : 
      81             : {
      82      153693 :     const auto oIter = oMetadata.find(SanitizeDomain(pszDomain));
      83      153693 :     if (oIter == oMetadata.end())
      84      119119 :         return nullptr;
      85       34574 :     return oIter->second.List();
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                            SetMetadata()                             */
      90             : /************************************************************************/
      91             : 
      92       21151 : CPLErr GDALMultiDomainMetadata::SetMetadata(CSLConstList papszMetadata,
      93             :                                             const char *pszDomain)
      94             : 
      95             : {
      96       21151 :     pszDomain = SanitizeDomain(pszDomain);
      97             : 
      98       21151 :     auto oIter = oMetadata.find(pszDomain);
      99       21151 :     if (oIter == oMetadata.end())
     100             :     {
     101       19035 :         aosDomainList.AddString(pszDomain);
     102       19035 :         oIter =
     103       19035 :             oMetadata.insert(std::pair(aosDomainList.back(), CPLStringList()))
     104             :                 .first;
     105             :     }
     106             : 
     107       21151 :     auto &oMDList = oIter->second;
     108       21151 :     oMDList = papszMetadata;
     109             : 
     110             :     // we want to mark name/value pair domains as being sorted for fast
     111             :     // access.
     112       21151 :     if (!STARTS_WITH_CI(pszDomain, "xml:") &&
     113       20213 :         !STARTS_WITH_CI(pszDomain, "json:") && !EQUAL(pszDomain, "SUBDATASETS"))
     114             :     {
     115       19923 :         oMDList.Sort();
     116             :     }
     117             : 
     118       21151 :     return CE_None;
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                          GetMetadataItem()                           */
     123             : /************************************************************************/
     124             : 
     125    10454100 : const char *GDALMultiDomainMetadata::GetMetadataItem(const char *pszName,
     126             :                                                      const char *pszDomain)
     127             : 
     128             : {
     129    10454100 :     const auto oIter = oMetadata.find(SanitizeDomain(pszDomain));
     130    10479000 :     if (oIter == oMetadata.end())
     131      186541 :         return nullptr;
     132    10292800 :     return oIter->second.FetchNameValue(pszName);
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /*                          SetMetadataItem()                           */
     137             : /************************************************************************/
     138             : 
     139     3304460 : CPLErr GDALMultiDomainMetadata::SetMetadataItem(const char *pszName,
     140             :                                                 const char *pszValue,
     141             :                                                 const char *pszDomain)
     142             : 
     143             : {
     144     3304460 :     pszDomain = SanitizeDomain(pszDomain);
     145             : 
     146             :     /* -------------------------------------------------------------------- */
     147             :     /*      Create the domain if it does not already exist.                 */
     148             :     /* -------------------------------------------------------------------- */
     149             : 
     150     3304440 :     auto oIter = oMetadata.find(pszDomain);
     151     3304420 :     if (oIter == oMetadata.end())
     152             :     {
     153      374380 :         aosDomainList.AddString(pszDomain);
     154      374377 :         oIter =
     155      374377 :             oMetadata.insert(std::pair(aosDomainList.back(), CPLStringList()))
     156             :                 .first;
     157             :     }
     158             : 
     159             :     /* -------------------------------------------------------------------- */
     160             :     /*      Set the value in the domain list.                               */
     161             :     /* -------------------------------------------------------------------- */
     162     3304430 :     oIter->second.SetNameValue(pszName, pszValue);
     163             : 
     164     3304440 :     return CE_None;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                              XMLInit()                               */
     169             : /*                                                                      */
     170             : /*      This method should be invoked on the parent of the              */
     171             : /*      <Metadata> elements.                                            */
     172             : /************************************************************************/
     173             : 
     174        6227 : int GDALMultiDomainMetadata::XMLInit(const CPLXMLNode *psTree, int /* bMerge */)
     175             : {
     176        6227 :     const CPLXMLNode *psMetadata = nullptr;
     177             : 
     178             :     /* ==================================================================== */
     179             :     /*      Process all <Metadata> elements, each for one domain.           */
     180             :     /* ==================================================================== */
     181       27858 :     for (psMetadata = psTree->psChild; psMetadata != nullptr;
     182       21631 :          psMetadata = psMetadata->psNext)
     183             :     {
     184       21631 :         if (psMetadata->eType != CXT_Element ||
     185       13836 :             !EQUAL(psMetadata->pszValue, "Metadata"))
     186       18900 :             continue;
     187             : 
     188        2731 :         const char *pszDomain = CPLGetXMLValue(psMetadata, "domain", "");
     189        2731 :         const char *pszFormat = CPLGetXMLValue(psMetadata, "format", "");
     190             : 
     191             :         // Make sure we have a CPLStringList for this domain,
     192             :         // without wiping out an existing one.
     193        2731 :         if (GetMetadata(pszDomain) == nullptr)
     194        1935 :             SetMetadata(nullptr, pszDomain);
     195             : 
     196        2731 :         auto oIter = oMetadata.find(pszDomain);
     197        2731 :         CPLAssert(oIter != oMetadata.end());
     198             : 
     199        2731 :         auto &oMDList = oIter->second;
     200             : 
     201             :         /* --------------------------------------------------------------------
     202             :          */
     203             :         /*      XML format subdocuments. */
     204             :         /* --------------------------------------------------------------------
     205             :          */
     206        2731 :         if (EQUAL(pszFormat, "xml"))
     207             :         {
     208             :             // Find first non-attribute child of current element.
     209          26 :             const CPLXMLNode *psSubDoc = psMetadata->psChild;
     210          78 :             while (psSubDoc != nullptr && psSubDoc->eType == CXT_Attribute)
     211          52 :                 psSubDoc = psSubDoc->psNext;
     212             : 
     213          26 :             char *pszDoc = CPLSerializeXMLTree(psSubDoc);
     214             : 
     215          26 :             oMDList.Clear();
     216          26 :             oMDList.AddStringDirectly(pszDoc);
     217             :         }
     218             : 
     219             :         /* --------------------------------------------------------------------
     220             :          */
     221             :         /*      JSon format subdocuments. */
     222             :         /* --------------------------------------------------------------------
     223             :          */
     224        2705 :         else if (EQUAL(pszFormat, "json"))
     225             :         {
     226             :             // Find first text child of current element.
     227          11 :             const CPLXMLNode *psSubDoc = psMetadata->psChild;
     228          33 :             while (psSubDoc != nullptr && psSubDoc->eType != CXT_Text)
     229          22 :                 psSubDoc = psSubDoc->psNext;
     230          11 :             if (psSubDoc)
     231             :             {
     232          11 :                 oMDList.Clear();
     233          11 :                 oMDList.AddString(psSubDoc->pszValue);
     234             :             }
     235             :         }
     236             : 
     237             :         /* --------------------------------------------------------------------
     238             :          */
     239             :         /*      Name value format. */
     240             :         /*      <MDI key="...">value_Text</MDI> */
     241             :         /* --------------------------------------------------------------------
     242             :          */
     243             :         else
     244             :         {
     245        2694 :             for (const CPLXMLNode *psMDI = psMetadata->psChild;
     246       16370 :                  psMDI != nullptr; psMDI = psMDI->psNext)
     247             :             {
     248       13676 :                 if (!EQUAL(psMDI->pszValue, "MDI") ||
     249       12636 :                     psMDI->eType != CXT_Element || psMDI->psChild == nullptr ||
     250       12636 :                     psMDI->psChild->psNext == nullptr ||
     251       10890 :                     psMDI->psChild->eType != CXT_Attribute ||
     252       10890 :                     psMDI->psChild->psChild == nullptr)
     253        2786 :                     continue;
     254             : 
     255       10890 :                 char *pszName = psMDI->psChild->psChild->pszValue;
     256       10890 :                 char *pszValue = psMDI->psChild->psNext->pszValue;
     257       10890 :                 if (pszName != nullptr && pszValue != nullptr)
     258       10890 :                     oMDList.SetNameValue(pszName, pszValue);
     259             :             }
     260             :         }
     261             :     }
     262             : 
     263        6227 :     return !aosDomainList.empty();
     264             : }
     265             : 
     266             : /************************************************************************/
     267             : /*                             Serialize()                              */
     268             : /************************************************************************/
     269             : 
     270        5320 : CPLXMLNode *GDALMultiDomainMetadata::Serialize() const
     271             : 
     272             : {
     273        5320 :     CPLXMLNode *psFirst = nullptr;
     274             : 
     275        9356 :     for (const auto &[pszDomainName, oList] : oMetadata)
     276             :     {
     277        4036 :         CSLConstList papszMD = oList.List();
     278             :         // Do not serialize empty domains.
     279        4036 :         if (papszMD == nullptr || papszMD[0] == nullptr)
     280        1318 :             continue;
     281             : 
     282        2718 :         CPLXMLNode *psMD = CPLCreateXMLNode(nullptr, CXT_Element, "Metadata");
     283             : 
     284        2718 :         if (strlen(pszDomainName) > 0)
     285        1085 :             CPLCreateXMLNode(CPLCreateXMLNode(psMD, CXT_Attribute, "domain"),
     286             :                              CXT_Text, pszDomainName);
     287             : 
     288        2718 :         bool bFormatXMLOrJSon = false;
     289             : 
     290        2718 :         if (STARTS_WITH_CI(pszDomainName, "xml:") && CSLCount(papszMD) == 1)
     291             :         {
     292          22 :             CPLXMLNode *psValueAsXML = CPLParseXMLString(papszMD[0]);
     293          22 :             if (psValueAsXML != nullptr)
     294             :             {
     295          22 :                 bFormatXMLOrJSon = true;
     296             : 
     297          22 :                 CPLCreateXMLNode(
     298             :                     CPLCreateXMLNode(psMD, CXT_Attribute, "format"), CXT_Text,
     299             :                     "xml");
     300             : 
     301          22 :                 CPLAddXMLChild(psMD, psValueAsXML);
     302             :             }
     303             :         }
     304             : 
     305        2718 :         if (STARTS_WITH_CI(pszDomainName, "json:") && CSLCount(papszMD) == 1)
     306             :         {
     307           5 :             bFormatXMLOrJSon = true;
     308             : 
     309           5 :             CPLCreateXMLNode(CPLCreateXMLNode(psMD, CXT_Attribute, "format"),
     310             :                              CXT_Text, "json");
     311           5 :             CPLCreateXMLNode(psMD, CXT_Text, *papszMD);
     312             :         }
     313             : 
     314        2718 :         if (!bFormatXMLOrJSon)
     315             :         {
     316        2691 :             CPLXMLNode *psLastChild = nullptr;
     317             :             // To go after domain attribute.
     318        2691 :             if (psMD->psChild != nullptr)
     319             :             {
     320        1058 :                 psLastChild = psMD->psChild;
     321        1058 :                 while (psLastChild->psNext != nullptr)
     322           0 :                     psLastChild = psLastChild->psNext;
     323             :             }
     324       12784 :             for (int i = 0; papszMD[i] != nullptr; i++)
     325             :             {
     326       10093 :                 char *pszKey = nullptr;
     327             : 
     328             :                 const char *pszRawValue =
     329       10093 :                     CPLParseNameValue(papszMD[i], &pszKey);
     330             : 
     331             :                 CPLXMLNode *psMDI =
     332       10093 :                     CPLCreateXMLNode(nullptr, CXT_Element, "MDI");
     333       10093 :                 if (psLastChild == nullptr)
     334        1633 :                     psMD->psChild = psMDI;
     335             :                 else
     336        8460 :                     psLastChild->psNext = psMDI;
     337       10093 :                 psLastChild = psMDI;
     338             : 
     339       10093 :                 CPLSetXMLValue(psMDI, "#key", pszKey);
     340       10093 :                 CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
     341             : 
     342       10093 :                 CPLFree(pszKey);
     343             :             }
     344             :         }
     345             : 
     346        2718 :         if (psFirst == nullptr)
     347        2104 :             psFirst = psMD;
     348             :         else
     349         614 :             CPLAddXMLSibling(psFirst, psMD);
     350             :     }
     351             : 
     352        5320 :     return psFirst;
     353             : }
     354             : 
     355             : //! @endcond

Generated by: LCOV version 1.14