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