1 : /**********************************************************************
2 : * $Id: nashandler.cpp 24105 2012-03-10 12:08:04Z rouault $
3 : *
4 : * Project: NAS Reader
5 : * Purpose: Implementation of NASHandler class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : **********************************************************************
9 : * Copyright (c) 2002, Frank Warmerdam
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 <ctype.h>
31 : #include "nasreaderp.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 :
35 : #define MAX_TOKEN_SIZE 1000
36 :
37 : /************************************************************************/
38 : /* NASHandler() */
39 : /************************************************************************/
40 :
41 18 : NASHandler::NASHandler( NASReader *poReader )
42 :
43 : {
44 18 : m_poReader = poReader;
45 18 : m_pszCurField = NULL;
46 18 : m_pszGeometry = NULL;
47 18 : m_nGeomAlloc = m_nGeomLen = 0;
48 18 : m_nDepthFeature = m_nDepth = 0;
49 18 : m_bIgnoreFeature = FALSE;
50 18 : }
51 :
52 : /************************************************************************/
53 : /* ~NASHandler() */
54 : /************************************************************************/
55 :
56 18 : NASHandler::~NASHandler()
57 :
58 : {
59 18 : CPLFree( m_pszCurField );
60 18 : CPLFree( m_pszGeometry );
61 18 : }
62 :
63 : /************************************************************************/
64 : /* GetAttributes() */
65 : /************************************************************************/
66 :
67 899488 : CPLString NASHandler::GetAttributes(const Attributes* attrs)
68 : {
69 899488 : CPLString osRes;
70 : char *pszString;
71 :
72 919298 : for(unsigned int i=0; i < attrs->getLength(); i++)
73 : {
74 19810 : osRes += " ";
75 19810 : pszString = tr_strdup(attrs->getQName(i));
76 19810 : osRes += pszString;
77 19810 : CPLFree( pszString );
78 19810 : osRes += "=\"";
79 19810 : pszString = tr_strdup(attrs->getValue(i));
80 19810 : osRes += pszString;
81 19810 : CPLFree( pszString );
82 19810 : osRes += "\"";
83 : }
84 0 : return osRes;
85 : }
86 :
87 :
88 : /************************************************************************/
89 : /* startElement() */
90 : /************************************************************************/
91 :
92 4470626 : void NASHandler::startElement(const XMLCh* const uri,
93 : const XMLCh* const localname,
94 : const XMLCh* const qname,
95 : const Attributes& attrs )
96 :
97 : {
98 : char szElementName[MAX_TOKEN_SIZE];
99 4470626 : GMLReadState *poState = m_poReader->GetState();
100 4470626 : const char *pszLast = NULL;
101 :
102 4470626 : tr_strcpy( szElementName, localname );
103 :
104 4470626 : if (m_bIgnoreFeature && m_nDepth >= m_nDepthFeature)
105 : {
106 3123872 : m_nDepth ++;
107 3123872 : return;
108 : }
109 :
110 : #ifdef DEBUG_VERBOSE
111 : CPLDebug("NAS",
112 : "%*sstartElement %s m_bIgnoreFeature:%d depth:%d depthFeature:%d featureClass:%s",
113 : m_nDepth, "", szElementName,
114 : m_bIgnoreFeature, m_nDepth, m_nDepthFeature,
115 : poState->m_poFeature ? poState->m_poFeature->GetClass()->GetElementName() : "(no feature)"
116 : );
117 : #endif
118 :
119 : /* -------------------------------------------------------------------- */
120 : /* If we are in the midst of collecting a feature attribute */
121 : /* value, then this must be a complex attribute which we don't */
122 : /* try to collect for now, so just terminate the field */
123 : /* collection. */
124 : /* -------------------------------------------------------------------- */
125 1346754 : if( m_pszCurField != NULL )
126 : {
127 100 : CPLFree( m_pszCurField );
128 100 : m_pszCurField = NULL;
129 : }
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* If we are collecting geometry, or if we determine this is a */
133 : /* geometry element then append to the geometry info. */
134 : /* -------------------------------------------------------------------- */
135 1346754 : if( m_pszGeometry != NULL
136 : || IsGeometryElement( szElementName ) )
137 : {
138 899488 : int nLNLen = tr_strlen( localname );
139 899488 : CPLString osAttributes = GetAttributes( &attrs );
140 :
141 : /* should save attributes too! */
142 :
143 899488 : if( m_pszGeometry == NULL )
144 13368 : m_nGeometryDepth = poState->m_nPathLength;
145 :
146 899488 : if( m_nGeomLen + nLNLen + 4 + (int)osAttributes.size() > m_nGeomAlloc )
147 : {
148 13368 : m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nLNLen + osAttributes.size() + 1000);
149 : m_pszGeometry = (char *)
150 13368 : CPLRealloc( m_pszGeometry, m_nGeomAlloc);
151 : }
152 :
153 899488 : strcpy( m_pszGeometry+m_nGeomLen, "<" );
154 899488 : tr_strcpy( m_pszGeometry+m_nGeomLen+1, localname );
155 :
156 899488 : if( osAttributes.size() > 0 )
157 : {
158 19144 : strcat( m_pszGeometry+m_nGeomLen, " " );
159 19144 : strcat( m_pszGeometry+m_nGeomLen, osAttributes );
160 : }
161 :
162 899488 : strcat( m_pszGeometry+m_nGeomLen, ">" );
163 899488 : m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen);
164 : }
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Is this the ogc:Filter element in a wfs:Delete or */
168 : /* wfsext:Replace operation? If so we translate it as a */
169 : /* specialized sort of feature. */
170 : /* -------------------------------------------------------------------- */
171 447266 : else if( EQUAL(szElementName,"Filter")
172 : && (pszLast = m_poReader->GetState()->GetLastComponent()) != NULL
173 : && (EQUAL(pszLast,"Delete") || EQUAL(pszLast,"Replace")) )
174 : {
175 12 : const char* pszFilteredClassName = m_poReader->GetFilteredClassName();
176 12 : if ( pszFilteredClassName != NULL &&
177 : strcmp("Delete", pszFilteredClassName) != 0 )
178 : {
179 0 : m_bIgnoreFeature = TRUE;
180 0 : m_nDepthFeature = m_nDepth;
181 0 : m_nDepth ++;
182 :
183 0 : return;
184 : }
185 :
186 12 : m_bIgnoreFeature = FALSE;
187 :
188 12 : m_poReader->PushFeature( "Delete", attrs );
189 :
190 12 : m_nDepthFeature = m_nDepth;
191 12 : m_nDepth ++;
192 :
193 12 : CPLAssert( m_osLastTypeName != "" );
194 12 : m_poReader->SetFeaturePropertyDirectly( "typeName", CPLStrdup(m_osLastTypeName) );
195 12 : m_poReader->SetFeaturePropertyDirectly( "context", CPLStrdup(pszLast) );
196 :
197 12 : if( EQUAL( pszLast, "Replace" ) )
198 : {
199 4 : CPLAssert( m_osLastReplacingFID != "" );
200 4 : CPLAssert( m_osLastSafeToIgnore != "" );
201 4 : m_poReader->SetFeaturePropertyDirectly( "replacedBy", CPLStrdup(m_osLastReplacingFID) );
202 4 : m_poReader->SetFeaturePropertyDirectly( "safeToIgnore", CPLStrdup(m_osLastSafeToIgnore) );
203 : }
204 :
205 12 : return;
206 : }
207 :
208 : /* -------------------------------------------------------------------- */
209 : /* Is it a feature? If so push a whole new state, and return. */
210 : /* -------------------------------------------------------------------- */
211 447254 : else if( m_poReader->IsFeatureElement( szElementName ) )
212 : {
213 139292 : m_osLastTypeName = szElementName;
214 :
215 139292 : const char* pszFilteredClassName = m_poReader->GetFilteredClassName();
216 :
217 139292 : pszLast = m_poReader->GetState()->GetLastComponent();
218 278584 : if( pszLast != NULL && EQUAL(pszLast,"Replace") )
219 : {
220 : int nIndex;
221 : XMLCh Name[100];
222 :
223 6 : tr_strcpy( Name, "gml:id" );
224 6 : nIndex = attrs.getIndex( Name );
225 :
226 6 : CPLAssert( nIndex!=-1 );
227 6 : CPLAssert( m_osLastReplacingFID=="" );
228 :
229 : // Capture "gml:id" attribute as part of the property value -
230 : // primarily this is for the wfsext:Replace operation's attribute.
231 6 : char *pszReplacingFID = tr_strdup( attrs.getValue( nIndex ) );
232 6 : m_osLastReplacingFID = pszReplacingFID;
233 6 : CPLFree( pszReplacingFID );
234 :
235 : #ifdef DEBUG_VERBOSE
236 : CPLDebug("NAS", "%*s### Replace typeName=%s replacedBy=%s", m_nDepth, "", m_osLastTypeName.c_str(), m_osLastReplacingFID.c_str() );
237 : #endif
238 : }
239 :
240 139292 : if ( pszFilteredClassName != NULL &&
241 : strcmp(szElementName, pszFilteredClassName) != 0 )
242 : {
243 121836 : m_bIgnoreFeature = TRUE;
244 121836 : m_nDepthFeature = m_nDepth;
245 121836 : m_nDepth ++;
246 :
247 121836 : return;
248 : }
249 :
250 17456 : m_bIgnoreFeature = FALSE;
251 :
252 17456 : m_poReader->PushFeature( szElementName, attrs );
253 :
254 17456 : m_nDepthFeature = m_nDepth;
255 17456 : m_nDepth ++;
256 :
257 17456 : return;
258 : }
259 :
260 : /* -------------------------------------------------------------------- */
261 : /* If it is the wfs:Delete element, then remember the typeName */
262 : /* attribute so we can assign it to the feature that will be */
263 : /* produced when we process the Filter element. */
264 : /* -------------------------------------------------------------------- */
265 307962 : else if( EQUAL(szElementName,"Delete") )
266 : {
267 : int nIndex;
268 : XMLCh Name[100];
269 :
270 8 : tr_strcpy( Name, "typeName" );
271 8 : nIndex = attrs.getIndex( Name );
272 :
273 8 : if( nIndex != -1 )
274 : {
275 8 : char *pszTypeName = tr_strdup( attrs.getValue( nIndex ) );
276 8 : m_osLastTypeName = pszTypeName;
277 8 : CPLFree( pszTypeName );
278 : }
279 :
280 8 : m_osLastSafeToIgnore = "";
281 16 : m_osLastReplacingFID = "";
282 : }
283 :
284 : /* -------------------------------------------------------------------- */
285 : /* If it is the wfsext:Replace element, then remember the */
286 : /* safeToIgnore attribute so we can assign it to the feature */
287 : /* that will be produced when we process the Filter element. */
288 : /* -------------------------------------------------------------------- */
289 307954 : else if( EQUAL(szElementName,"Replace") )
290 : {
291 : int nIndex;
292 : XMLCh Name[100];
293 :
294 6 : tr_strcpy( Name, "safeToIgnore" );
295 6 : nIndex = attrs.getIndex( Name );
296 :
297 6 : if( nIndex != -1 )
298 : {
299 6 : char *pszSafeToIgnore = tr_strdup( attrs.getValue( nIndex ) );
300 6 : m_osLastSafeToIgnore = pszSafeToIgnore;
301 6 : CPLFree( pszSafeToIgnore );
302 : }
303 : else
304 : {
305 0 : CPLError( CE_Warning, CPLE_AppDefined, "NAS: safeToIgnore attribute missing" );
306 0 : m_osLastSafeToIgnore = "false";
307 : }
308 :
309 6 : m_osLastReplacingFID = "";
310 : }
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* If it is (or at least potentially is) a simple attribute, */
314 : /* then start collecting it. */
315 : /* -------------------------------------------------------------------- */
316 307948 : else if( m_poReader->IsAttributeElement( szElementName ) )
317 : {
318 77158 : CPLFree( m_pszCurField );
319 77158 : m_pszCurField = CPLStrdup("");
320 :
321 : // Capture href as OB property.
322 77158 : m_poReader->CheckForRelations( szElementName, attrs );
323 :
324 : // Capture "fid" attribute as part of the property value -
325 : // primarily this is for wfs:Delete operation's FeatureId attribute.
326 77158 : if( EQUAL(szElementName,"FeatureId") )
327 12 : m_poReader->CheckForFID( attrs, &m_pszCurField );
328 : }
329 :
330 : /* -------------------------------------------------------------------- */
331 : /* Push the element onto the current state's path. */
332 : /* -------------------------------------------------------------------- */
333 1207450 : poState->PushPath( szElementName );
334 :
335 1207450 : m_nDepth ++;
336 : }
337 :
338 : /************************************************************************/
339 : /* endElement() */
340 : /************************************************************************/
341 4470588 : void NASHandler::endElement(const XMLCh* const uri,
342 : const XMLCh* const localname,
343 : const XMLCh* const qname )
344 :
345 : {
346 : char szElementName[MAX_TOKEN_SIZE];
347 4470588 : GMLReadState *poState = m_poReader->GetState();
348 :
349 4470588 : tr_strcpy( szElementName, localname );
350 :
351 :
352 4470588 : m_nDepth --;
353 :
354 4470588 : if (m_bIgnoreFeature && m_nDepth >= m_nDepthFeature)
355 : {
356 3245708 : if (m_nDepth == m_nDepthFeature)
357 : {
358 121836 : m_bIgnoreFeature = FALSE;
359 121836 : m_nDepthFeature = 0;
360 : }
361 3245708 : return;
362 : }
363 :
364 : #ifdef DEBUG_VERBOSE
365 : CPLDebug("NAS",
366 : "%*sendElement %s m_bIgnoreFeature:%d depth:%d depthFeature:%d featureClass:%s",
367 : m_nDepth, "", szElementName,
368 : m_bIgnoreFeature, m_nDepth, m_nDepthFeature,
369 : poState->m_poFeature ? poState->m_poFeature->GetClass()->GetElementName() : "(no feature)"
370 : );
371 : #endif
372 :
373 : /* -------------------------------------------------------------------- */
374 : /* Is this closing off an attribute value? We assume so if */
375 : /* we are collecting an attribute value and got to this point. */
376 : /* We don't bother validating that the closing tag matches the */
377 : /* opening tag. */
378 : /* -------------------------------------------------------------------- */
379 1224880 : if( m_pszCurField != NULL )
380 : {
381 77058 : CPLAssert( poState->m_poFeature != NULL );
382 :
383 77058 : m_poReader->SetFeaturePropertyDirectly( poState->osPath.c_str(), m_pszCurField );
384 77058 : m_pszCurField = NULL;
385 : }
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* If we are collecting Geometry than store it, and consider if */
389 : /* this is the end of the geometry. */
390 : /* -------------------------------------------------------------------- */
391 1224880 : if( m_pszGeometry != NULL )
392 : {
393 899488 : int nLNLen = tr_strlen( localname );
394 :
395 : /* should save attributes too! */
396 :
397 899488 : if( m_nGeomLen + nLNLen + 4 > m_nGeomAlloc )
398 : {
399 0 : m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nLNLen + 1000);
400 : m_pszGeometry = (char *)
401 0 : CPLRealloc( m_pszGeometry, m_nGeomAlloc);
402 : }
403 :
404 899488 : strcat( m_pszGeometry+m_nGeomLen, "</" );
405 899488 : tr_strcpy( m_pszGeometry+m_nGeomLen+2, localname );
406 899488 : strcat( m_pszGeometry+m_nGeomLen+nLNLen+2, ">" );
407 899488 : m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen);
408 :
409 899488 : if( poState->m_nPathLength == m_nGeometryDepth+1 )
410 : {
411 13368 : if( poState->m_poFeature != NULL )
412 : {
413 13368 : CPLXMLNode* psNode = CPLParseXMLString(m_pszGeometry);
414 13368 : if (psNode)
415 : {
416 : /* workaround common malformed gml:pos with just a
417 : * elevation value instead of a full 3D coordinate:
418 : *
419 : * <gml:Point gml:id="BII2H">
420 : * <gml:pos srsName="urn:adv:crs:ETRS89_h">41.394</gml:pos>
421 : * </gml:Point>
422 : *
423 : */
424 : const char *pszPos;
425 13368 : if( (pszPos = CPLGetXMLValue( psNode, "=Point.pos", NULL ) ) != NULL
426 : && strstr(pszPos, " ") == NULL )
427 : {
428 0 : CPLSetXMLValue( psNode, "pos", CPLSPrintf("0 0 %s", pszPos) );
429 : }
430 :
431 13368 : poState->m_poFeature->SetGeometryDirectly( psNode );
432 : }
433 : }
434 :
435 13368 : CPLFree( m_pszGeometry );
436 13368 : m_pszGeometry = NULL;
437 13368 : m_nGeomAlloc = m_nGeomLen = 0;
438 : }
439 : }
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* If we are collecting a feature, and this element tag matches */
443 : /* element name for the class, then we have finished the */
444 : /* feature, and we pop the feature read state. */
445 : /* -------------------------------------------------------------------- */
446 1224880 : if( m_nDepth == m_nDepthFeature && poState->m_poFeature != NULL
447 : && EQUAL(szElementName,
448 : poState->m_poFeature->GetClass()->GetElementName()) )
449 : {
450 17456 : m_nDepthFeature = 0;
451 17456 : m_poReader->PopState();
452 : }
453 :
454 : /* -------------------------------------------------------------------- */
455 : /* Ends of a wfs:Delete should be triggered on the close of the */
456 : /* <Filter> element. */
457 : /* -------------------------------------------------------------------- */
458 1207424 : else if( m_nDepth == m_nDepthFeature
459 : && poState->m_poFeature != NULL
460 : && EQUAL(szElementName,"Filter")
461 : && EQUAL(poState->m_poFeature->GetClass()->GetElementName(),
462 : "Delete") )
463 : {
464 12 : m_nDepthFeature = 0;
465 12 : m_poReader->PopState();
466 : }
467 :
468 : /* -------------------------------------------------------------------- */
469 : /* Otherwise, we just pop the element off the local read states */
470 : /* element stack. */
471 : /* -------------------------------------------------------------------- */
472 : else
473 : {
474 1207412 : if( EQUAL(szElementName,poState->GetLastComponent()) )
475 1207412 : poState->PopPath();
476 : else
477 : {
478 0 : CPLAssert( FALSE );
479 : }
480 : }
481 : }
482 :
483 : /************************************************************************/
484 : /* characters() */
485 : /************************************************************************/
486 :
487 : #if XERCES_VERSION_MAJOR >= 3
488 : void NASHandler::characters( const XMLCh *const chars_in,
489 : const XMLSize_t length )
490 : #else
491 8834374 : void NASHandler::characters(const XMLCh* const chars_in,
492 : const unsigned int length )
493 : #endif
494 :
495 : {
496 8834374 : const XMLCh *chars = chars_in;
497 :
498 8834374 : if( m_pszCurField != NULL )
499 : {
500 72130 : int nCurFieldLength = strlen(m_pszCurField);
501 :
502 72130 : if (nCurFieldLength == 0)
503 : {
504 : // Ignore white space
505 145056 : while( *chars == ' ' || *chars == 10 || *chars == 13 || *chars == '\t')
506 796 : chars++;
507 : }
508 :
509 72130 : char *pszTranslated = tr_strdup(chars);
510 :
511 72130 : if( m_pszCurField == NULL )
512 : {
513 0 : m_pszCurField = pszTranslated;
514 0 : nCurFieldLength = strlen(m_pszCurField);
515 : }
516 : else
517 : {
518 : m_pszCurField = (char *)
519 : CPLRealloc( m_pszCurField,
520 72130 : nCurFieldLength+strlen(pszTranslated)+1 );
521 72130 : strcpy( m_pszCurField + nCurFieldLength, pszTranslated );
522 72130 : CPLFree( pszTranslated );
523 : }
524 : }
525 8762244 : else if( m_pszGeometry != NULL )
526 : {
527 1785608 : if (m_nGeomLen == 0)
528 : {
529 : // Ignore white space
530 0 : while( *chars == ' ' || *chars == 10 || *chars == 13 || *chars == '\t')
531 0 : chars++;
532 : }
533 :
534 1785608 : int nCharsLen = tr_strlen(chars);
535 :
536 1785608 : if( m_nGeomLen + nCharsLen*4 + 4 > m_nGeomAlloc )
537 : {
538 23932 : m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nCharsLen*4 + 1000);
539 : m_pszGeometry = (char *)
540 23932 : CPLRealloc( m_pszGeometry, m_nGeomAlloc);
541 : }
542 :
543 1785608 : tr_strcpy( m_pszGeometry+m_nGeomLen, chars );
544 1785608 : m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen);
545 : }
546 8834374 : }
547 :
548 : /************************************************************************/
549 : /* fatalError() */
550 : /************************************************************************/
551 :
552 0 : void NASHandler::fatalError( const SAXParseException &exception)
553 :
554 : {
555 : char *pszErrorMessage;
556 :
557 0 : pszErrorMessage = tr_strdup( exception.getMessage() );
558 : CPLError( CE_Failure, CPLE_AppDefined,
559 : "XML Parsing Error: %s\n",
560 0 : pszErrorMessage );
561 :
562 0 : CPLFree( pszErrorMessage );
563 0 : }
564 :
565 : /************************************************************************/
566 : /* IsGeometryElement() */
567 : /************************************************************************/
568 :
569 460634 : int NASHandler::IsGeometryElement( const char *pszElement )
570 :
571 : {
572 : return strcmp(pszElement,"Polygon") == 0
573 : || strcmp(pszElement,"MultiPolygon") == 0
574 : || strcmp(pszElement,"MultiPoint") == 0
575 : || strcmp(pszElement,"MultiLineString") == 0
576 : || strcmp(pszElement,"MultiSurface") == 0
577 : || strcmp(pszElement,"GeometryCollection") == 0
578 : || strcmp(pszElement,"Point") == 0
579 : || strcmp(pszElement,"Curve") == 0
580 : || strcmp(pszElement,"Surface") == 0
581 : || strcmp(pszElement,"PolygonPatch") == 0
582 460634 : || strcmp(pszElement,"LineString") == 0;
583 : }
|