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