1 : /******************************************************************************
2 : * $Id: ershdrnode.cpp 17867 2009-10-21 21:04:49Z rouault $
3 : *
4 : * Project: ERMapper .ers Driver
5 : * Purpose: Implementation of ERSHdrNode class for parsing/accessing .ers hdr.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.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
22 : * OR 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_conv.h"
31 : #include "cpl_string.h"
32 : #include "ershdrnode.h"
33 :
34 : CPL_CVSID("$Id: ershdrnode.cpp 17867 2009-10-21 21:04:49Z rouault $");
35 :
36 :
37 : /************************************************************************/
38 : /* ERSHdrNode() */
39 : /************************************************************************/
40 :
41 199 : ERSHdrNode::ERSHdrNode()
42 :
43 : {
44 199 : nItemMax = 0;
45 199 : nItemCount = 0;
46 199 : papszItemName = NULL;
47 199 : papszItemValue = NULL;
48 199 : papoItemChild = NULL;
49 199 : }
50 :
51 : /************************************************************************/
52 : /* ~ERSHdrNode() */
53 : /************************************************************************/
54 :
55 199 : ERSHdrNode::~ERSHdrNode()
56 :
57 : {
58 : int i;
59 :
60 1065 : for( i = 0; i < nItemCount; i++ )
61 : {
62 866 : if( papoItemChild[i] != NULL )
63 159 : delete papoItemChild[i];
64 866 : if( papszItemValue[i] != NULL )
65 707 : CPLFree( papszItemValue[i] );
66 866 : CPLFree( papszItemName[i] );
67 : }
68 :
69 199 : CPLFree( papszItemName );
70 199 : CPLFree( papszItemValue );
71 199 : CPLFree( papoItemChild );
72 199 : }
73 :
74 : /************************************************************************/
75 : /* MakeSpace() */
76 : /* */
77 : /* Ensure we have room for at least one more entry in our item */
78 : /* lists. */
79 : /************************************************************************/
80 :
81 866 : void ERSHdrNode::MakeSpace()
82 :
83 : {
84 866 : if( nItemCount == nItemMax )
85 : {
86 200 : nItemMax = (int) (nItemMax * 1.3) + 10;
87 : papszItemName = (char **)
88 200 : CPLRealloc(papszItemName,sizeof(char *) * nItemMax);
89 : papszItemValue = (char **)
90 200 : CPLRealloc(papszItemValue,sizeof(char *) * nItemMax);
91 : papoItemChild = (ERSHdrNode **)
92 200 : CPLRealloc(papoItemChild,sizeof(void *) * nItemMax);
93 : }
94 866 : }
95 :
96 : /************************************************************************/
97 : /* ReadLine() */
98 : /* */
99 : /* Read one virtual line from the input source. Multiple lines */
100 : /* will be appended for objects enclosed in {}. */
101 : /************************************************************************/
102 :
103 617 : int ERSHdrNode::ReadLine( FILE * fp, CPLString &osLine )
104 :
105 : {
106 : int nBracketLevel;
107 :
108 617 : osLine = "";
109 :
110 617 : do
111 : {
112 617 : const char *pszNewLine = CPLReadLineL( fp );
113 :
114 617 : if( pszNewLine == NULL )
115 0 : return FALSE;
116 :
117 617 : osLine += pszNewLine;
118 :
119 617 : int bInQuote = FALSE;
120 : size_t i;
121 :
122 617 : nBracketLevel = 0;
123 :
124 12827 : for( i = 0; i < osLine.length(); i++ )
125 : {
126 12210 : if( osLine[i] == '"' )
127 208 : bInQuote = !bInQuote;
128 12002 : else if( osLine[i] == '{' && !bInQuote )
129 0 : nBracketLevel++;
130 12002 : else if( osLine[i] == '}' && !bInQuote )
131 0 : nBracketLevel--;
132 :
133 : // We have to ignore escaped quotes and backslashes in strings.
134 12002 : else if( osLine[i] == '\\' && osLine[i+1] == '"' && bInQuote )
135 0 : i++;
136 12002 : else if( osLine[i] == '\\' && osLine[i+1] == '\\' && bInQuote )
137 0 : i++;
138 : }
139 : } while( nBracketLevel > 0 );
140 :
141 617 : return TRUE;
142 : }
143 :
144 : /************************************************************************/
145 : /* ParseChildren() */
146 : /* */
147 : /* We receive the FILE * positioned after the "Object Begin" */
148 : /* line for this object, and are responsible for reading all */
149 : /* children. We should return after consuming the */
150 : /* corresponding End line for this object. Really the first */
151 : /* unmatched End since we don't know what object we are. */
152 : /* */
153 : /* This function is used recursively to read sub-objects. */
154 : /************************************************************************/
155 :
156 617 : int ERSHdrNode::ParseChildren( FILE * fp )
157 :
158 : {
159 0 : while( TRUE )
160 : {
161 : size_t iOff;
162 617 : CPLString osLine;
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Read the next line (or multi-line for bracketed value). */
166 : /* -------------------------------------------------------------------- */
167 617 : if( !ReadLine( fp, osLine ) )
168 0 : return FALSE;
169 :
170 : /* -------------------------------------------------------------------- */
171 : /* Got a Name=Value. */
172 : /* -------------------------------------------------------------------- */
173 617 : if( (iOff = osLine.find_first_of( '=' )) != std::string::npos )
174 : {
175 437 : CPLString osName = osLine.substr(0,iOff-1);
176 437 : osName.Trim();
177 :
178 437 : CPLString osValue = osLine.c_str() + iOff + 1;
179 437 : osValue.Trim();
180 :
181 437 : MakeSpace();
182 437 : papszItemName[nItemCount] = CPLStrdup(osName);
183 437 : papszItemValue[nItemCount] = CPLStrdup(osValue);
184 437 : papoItemChild[nItemCount] = NULL;
185 :
186 437 : nItemCount++;
187 : }
188 :
189 : /* -------------------------------------------------------------------- */
190 : /* Got a Begin for an object. */
191 : /* -------------------------------------------------------------------- */
192 180 : else if( (iOff = osLine.find( " Begin" )) != std::string::npos )
193 : {
194 69 : CPLString osName = osLine.substr(0,iOff);
195 69 : osName.Trim();
196 :
197 69 : MakeSpace();
198 69 : papszItemName[nItemCount] = CPLStrdup(osName);
199 69 : papszItemValue[nItemCount] = NULL;
200 69 : papoItemChild[nItemCount] = new ERSHdrNode();
201 :
202 69 : nItemCount++;
203 :
204 138 : if( !papoItemChild[nItemCount-1]->ParseChildren( fp ) )
205 0 : return FALSE;
206 : }
207 :
208 : /* -------------------------------------------------------------------- */
209 : /* Got an End for our object. Well, at least we *assume* it */
210 : /* must be for our object. */
211 : /* -------------------------------------------------------------------- */
212 111 : else if( osLine.find( " End" ) != std::string::npos )
213 : {
214 109 : return TRUE;
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Error? */
219 : /* -------------------------------------------------------------------- */
220 2 : else if( osLine.Trim().length() > 0 )
221 : {
222 : CPLError( CE_Failure, CPLE_AppDefined,
223 : "Unexpected line parsing .ecw:\n%s",
224 0 : osLine.c_str() );
225 0 : return FALSE;
226 : }
227 : }
228 : }
229 :
230 : /************************************************************************/
231 : /* WriteSelf() */
232 : /* */
233 : /* Recursively write self and children to file. */
234 : /************************************************************************/
235 :
236 150 : int ERSHdrNode::WriteSelf( FILE * fp, int nIndent )
237 :
238 : {
239 150 : CPLString oIndent;
240 : int i;
241 :
242 150 : oIndent.assign( nIndent, '\t' );
243 :
244 810 : for( i = 0; i < nItemCount; i++ )
245 : {
246 660 : if( papszItemValue[i] != NULL )
247 : {
248 540 : if( VSIFPrintfL( fp, "%s%s\t= %s\n",
249 : oIndent.c_str(),
250 : papszItemName[i],
251 : papszItemValue[i] ) < 1 )
252 0 : return FALSE;
253 : }
254 : else
255 : {
256 : VSIFPrintfL( fp, "%s%s Begin\n",
257 120 : oIndent.c_str(), papszItemName[i] );
258 120 : if( !papoItemChild[i]->WriteSelf( fp, nIndent+1 ) )
259 0 : return FALSE;
260 120 : if( VSIFPrintfL( fp, "%s%s End\n",
261 : oIndent.c_str(), papszItemName[i] ) < 1 )
262 0 : return FALSE;
263 : }
264 : }
265 :
266 150 : return TRUE;
267 : }
268 :
269 : /************************************************************************/
270 : /* Find() */
271 : /* */
272 : /* Find the desired entry value. The input is a path with */
273 : /* components seperated by dots, relative to the current node. */
274 : /************************************************************************/
275 :
276 1418 : const char *ERSHdrNode::Find( const char *pszPath, const char *pszDefault )
277 :
278 : {
279 : int i;
280 :
281 : /* -------------------------------------------------------------------- */
282 : /* If this is the final component of the path, search for a */
283 : /* matching child and return the value. */
284 : /* -------------------------------------------------------------------- */
285 1418 : if( strchr(pszPath,'.') == NULL )
286 : {
287 2601 : for( i = 0; i < nItemCount; i++ )
288 : {
289 2402 : if( EQUAL(pszPath,papszItemName[i]) )
290 : {
291 477 : if( papszItemValue[i] != NULL )
292 : {
293 477 : if( papszItemValue[i][0] == '"' )
294 : {
295 : // strip off quotes.
296 25 : osTempReturn = papszItemValue[i];
297 : osTempReturn =
298 50 : osTempReturn.substr( 1, osTempReturn.length()-2 );
299 25 : return osTempReturn;
300 : }
301 : else
302 452 : return papszItemValue[i];
303 : }
304 : else
305 0 : return pszDefault;
306 : }
307 : }
308 199 : return pszDefault;
309 : }
310 :
311 : /* -------------------------------------------------------------------- */
312 : /* This is a dot path - extract the first element, find a match */
313 : /* and recurse. */
314 : /* -------------------------------------------------------------------- */
315 742 : CPLString osPathFirst, osPathRest, osPath = pszPath;
316 : int iDot;
317 :
318 742 : iDot = osPath.find_first_of('.');
319 742 : osPathFirst = osPath.substr(0,iDot);
320 742 : osPathRest = osPath.substr(iDot+1);
321 :
322 4616 : for( i = 0; i < nItemCount; i++ )
323 : {
324 4451 : if( EQUAL(osPathFirst,papszItemName[i]) )
325 : {
326 577 : if( papoItemChild[i] != NULL )
327 1319 : return papoItemChild[i]->Find( osPathRest, pszDefault );
328 : else
329 0 : return pszDefault;
330 : }
331 : }
332 :
333 165 : return pszDefault;
334 : }
335 :
336 : /************************************************************************/
337 : /* FindElem() */
338 : /* */
339 : /* Find a particular element from an array valued item. */
340 : /************************************************************************/
341 :
342 : const char *ERSHdrNode::FindElem( const char *pszPath, int iElem,
343 0 : const char *pszDefault )
344 :
345 : {
346 0 : const char *pszArray = Find( pszPath, NULL );
347 : char **papszTokens;
348 0 : int bDefault = TRUE;
349 :
350 0 : if( pszArray == NULL )
351 0 : return pszDefault;
352 :
353 0 : papszTokens = CSLTokenizeStringComplex( pszArray, "{ \t}", TRUE, FALSE );
354 0 : if( iElem >= 0 && iElem < CSLCount(papszTokens) )
355 : {
356 0 : osTempReturn = papszTokens[iElem];
357 0 : bDefault = FALSE;
358 : }
359 :
360 0 : CSLDestroy( papszTokens );
361 :
362 0 : if( bDefault )
363 0 : return pszDefault;
364 : else
365 0 : return osTempReturn;
366 : }
367 :
368 : /************************************************************************/
369 : /* FindNode() */
370 : /* */
371 : /* Find the desired node. */
372 : /************************************************************************/
373 :
374 510 : ERSHdrNode *ERSHdrNode::FindNode( const char *pszPath )
375 :
376 : {
377 : int i;
378 510 : CPLString osPathFirst, osPathRest, osPath = pszPath;
379 : int iDot;
380 :
381 510 : iDot = osPath.find_first_of('.');
382 510 : if( iDot == -1 )
383 : {
384 470 : osPathFirst = osPath;
385 : }
386 : else
387 : {
388 40 : osPathFirst = osPath.substr(0,iDot);
389 40 : osPathRest = osPath.substr(iDot+1);
390 : }
391 :
392 3157 : for( i = 0; i < nItemCount; i++ )
393 : {
394 3027 : if( EQUAL(osPathFirst,papszItemName[i]) )
395 : {
396 380 : if( papoItemChild[i] != NULL )
397 : {
398 380 : if( osPathRest.length() > 0 )
399 40 : return papoItemChild[i]->FindNode( osPathRest );
400 : else
401 340 : return papoItemChild[i];
402 : }
403 : else
404 0 : return NULL;
405 : }
406 : }
407 :
408 130 : return NULL;
409 : }
410 :
411 : /************************************************************************/
412 : /* Set() */
413 : /* */
414 : /* Set a value item. */
415 : /************************************************************************/
416 :
417 660 : void ERSHdrNode::Set( const char *pszPath, const char *pszValue )
418 :
419 : {
420 660 : CPLString osPath = pszPath;
421 : int iDot;
422 :
423 660 : iDot = osPath.find_first_of('.');
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* We have an intermediate node, find or create it and */
427 : /* recurse. */
428 : /* -------------------------------------------------------------------- */
429 660 : if( iDot != -1 )
430 : {
431 390 : CPLString osPathFirst = osPath.substr(0,iDot);
432 390 : CPLString osPathRest = osPath.substr(iDot+1);
433 390 : ERSHdrNode *poFirst = FindNode( osPathFirst );
434 :
435 390 : if( poFirst == NULL )
436 : {
437 90 : poFirst = new ERSHdrNode();
438 :
439 90 : MakeSpace();
440 90 : papszItemName[nItemCount] = CPLStrdup(osPathFirst);
441 90 : papszItemValue[nItemCount] = NULL;
442 90 : papoItemChild[nItemCount] = poFirst;
443 90 : nItemCount++;
444 : }
445 :
446 390 : poFirst->Set( osPathRest, pszValue );
447 780 : return;
448 : }
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* This is the final item name. Find or create it. */
452 : /* -------------------------------------------------------------------- */
453 : int i;
454 :
455 630 : for( i = 0; i < nItemCount; i++ )
456 : {
457 360 : if( EQUAL(osPath,papszItemName[i])
458 : && papszItemValue[i] != NULL )
459 : {
460 0 : CPLFree( papszItemValue[i] );
461 0 : papszItemValue[i] = CPLStrdup( pszValue );
462 : return;
463 : }
464 : }
465 :
466 270 : MakeSpace();
467 270 : papszItemName[nItemCount] = CPLStrdup(osPath);
468 270 : papszItemValue[nItemCount] = CPLStrdup(pszValue);
469 270 : papoItemChild[nItemCount] = NULL;
470 270 : nItemCount++;
471 : }
|