1 /******************************************************************************
2  *
3  * Project:  Interlis 2 Reader
4  * Purpose:  Implementation of ILI2Reader class.
5  * Author:   Markus Schnider, Sourcepole AG
6  *
7  ******************************************************************************
8  * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG
9  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.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 "ili2readerp.h"
31 #include "ogr_ili2.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 #include "ili2reader.h"
36 
37 using namespace std;
38 
39 CPL_CVSID("$Id: ili2reader.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
40 
41 //
42 // constants
43 //
44 static const char * const ILI2_TID = "TID";
45 static const XMLCh xmlch_ILI2_TID[] = {'T', 'I', 'D', '\0' };
46 static const XMLCh ILI2_REF[] = {'R', 'E', 'F', '\0' };
47 
48 constexpr int ILI2_STRING_TYPE = 0;
49 constexpr int ILI2_COORD_TYPE = 1;
50 constexpr int ILI2_ARC_TYPE = 2;
51 constexpr int ILI2_POLYLINE_TYPE = 4;
52 constexpr int ILI2_BOUNDARY_TYPE = 8;
53 constexpr int ILI2_AREA_TYPE = 16; // also SURFACE
54 constexpr int ILI2_GEOMCOLL_TYPE = 32;
55 
56 static const char * const ILI2_COORD = "COORD";
57 static const char * const ILI2_ARC = "ARC";
58 static const char * const ILI2_POLYLINE = "POLYLINE";
59 static const char * const ILI2_BOUNDARY = "BOUNDARY";
60 static const char * const ILI2_AREA = "AREA";
61 static const char * const ILI2_SURFACE = "SURFACE";
62 
63 //
64 // helper functions
65 //
cmpStr(string s1,string s2)66 int cmpStr(string s1, string s2) {
67 
68   string::const_iterator p1 = s1.begin();
69   string::const_iterator p2 = s2.begin();
70 
71   while (p1 != s1.end() && p2 != s2.end()) {
72     if (toupper(*p1) != toupper(*p2))
73       return (toupper(*p1) < toupper(*p2)) ? -1 : 1;
74     ++p1;
75     ++p2;
76   }
77 
78   return (s2.size() == s1.size()) ? 0 :
79          (s1.size() < s2.size()) ? -1 : 1;
80 }
81 
ltrim(string tmpstr)82 string ltrim(string tmpstr) {
83   unsigned int i = 0;
84   while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' || tmpstr[i] == '\n')) ++i;
85   return i > 0 ? tmpstr.substr(i, tmpstr.length()-i) : tmpstr;
86 }
87 
rtrim(string tmpstr)88 string rtrim(string tmpstr) {
89   if (tmpstr.length() == 0) return tmpstr;
90   unsigned int i = static_cast<unsigned int>(tmpstr.length()) - 1;
91   while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' || tmpstr[i] == '\n') --i;
92   return i < tmpstr.length() - 1 ? tmpstr.substr(0, i+1) : tmpstr;
93 }
94 
trim(string tmpstr)95 string trim(string tmpstr) {
96   tmpstr = ltrim(tmpstr);
97   tmpstr = rtrim(tmpstr);
98   return tmpstr;
99 }
100 
getGeometryTypeOfElem(DOMElement * elem)101 static int getGeometryTypeOfElem(DOMElement* elem) {
102   int type = ILI2_STRING_TYPE;
103   if( elem == nullptr )
104       return type;
105   char* pszTagName = XMLString::transcode(elem->getTagName());
106 
107   if (elem->getNodeType() == DOMNode::ELEMENT_NODE) {
108     if (cmpStr(ILI2_COORD, pszTagName) == 0) {
109       type = ILI2_COORD_TYPE;
110     } else if (cmpStr(ILI2_ARC, pszTagName) == 0) {
111       type = ILI2_ARC_TYPE;
112     } else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) {
113       type = ILI2_POLYLINE_TYPE;
114     } else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) {
115       type = ILI2_BOUNDARY_TYPE;
116     } else if (cmpStr(ILI2_AREA, pszTagName) == 0) {
117       type = ILI2_AREA_TYPE;
118     } else if (cmpStr(ILI2_SURFACE, pszTagName) == 0) {
119       type = ILI2_AREA_TYPE;
120     }
121   }
122   XMLString::release(&pszTagName);
123   return type;
124 }
125 
getObjValue(DOMElement * elem)126 static char *getObjValue(DOMElement *elem) {
127   DOMNode* child = elem->getFirstChild();
128   if ((child != nullptr) && (child->getNodeType() == DOMNode::TEXT_NODE))
129   {
130     return CPLStrdup(transcode(child->getNodeValue()));
131   }
132 
133   return nullptr;
134 }
135 
getREFValue(DOMElement * elem)136 static char *getREFValue(DOMElement *elem) {
137   CPLString osREFValue(transcode(elem->getAttribute(ILI2_REF)));
138   return CPLStrdup(osREFValue);
139 }
140 
getPoint(DOMElement * elem)141 static OGRPoint *getPoint(DOMElement *elem) {
142   // elem -> COORD (or ARC)
143   DOMElement *coordElem = dynamic_cast<DOMElement*>(elem->getFirstChild());
144   if( coordElem == nullptr )
145       return nullptr;
146   OGRPoint *pt = new OGRPoint();
147 
148   while (coordElem != nullptr) {
149     char* pszTagName = XMLString::transcode(coordElem->getTagName());
150     char* pszObjValue = getObjValue(coordElem);
151     if( pszObjValue )
152     {
153         if (cmpStr("C1", pszTagName) == 0)
154         pt->setX(CPLAtof(pszObjValue));
155         else if (cmpStr("C2", pszTagName) == 0)
156         pt->setY(CPLAtof(pszObjValue));
157         else if (cmpStr("C3", pszTagName) == 0)
158         pt->setZ(CPLAtof(pszObjValue));
159     }
160     CPLFree(pszObjValue);
161     XMLString::release(&pszTagName);
162     coordElem = dynamic_cast<DOMElement*>(coordElem->getNextSibling());
163   }
164   pt->flattenTo2D();
165   return pt;
166 }
167 
getArc(DOMElement * elem)168 OGRCircularString *ILI2Reader::getArc(DOMElement *elem) {
169   // previous point -> start point
170   auto elemPrev = dynamic_cast<DOMElement*>(elem->getPreviousSibling());
171   if( elemPrev == nullptr )
172       return nullptr;
173   OGRPoint *ptStart = getPoint(elemPrev); // COORD or ARC
174   if( ptStart == nullptr )
175       return nullptr;
176 
177   // elem -> ARC
178   OGRCircularString *arc = new OGRCircularString();
179   // end point
180   OGRPoint *ptEnd = new OGRPoint();
181   // point on the arc
182   OGRPoint *ptOnArc = new OGRPoint();
183   // double radius = 0; // radius
184 
185   DOMElement *arcElem = dynamic_cast<DOMElement*>(elem->getFirstChild());
186   while (arcElem != nullptr) {
187     char* pszTagName = XMLString::transcode(arcElem->getTagName());
188     char* pszObjValue = getObjValue(arcElem);
189     if( pszObjValue )
190     {
191         if (cmpStr("C1", pszTagName) == 0)
192         ptEnd->setX(CPLAtof(pszObjValue));
193         else if (cmpStr("C2", pszTagName) == 0)
194         ptEnd->setY(CPLAtof(pszObjValue));
195         else if (cmpStr("C3", pszTagName) == 0)
196         ptEnd->setZ(CPLAtof(pszObjValue));
197         else if (cmpStr("A1", pszTagName) == 0)
198         ptOnArc->setX(CPLAtof(pszObjValue));
199         else if (cmpStr("A2", pszTagName) == 0)
200         ptOnArc->setY(CPLAtof(pszObjValue));
201         else if (cmpStr("A3", pszTagName) == 0)
202         ptOnArc->setZ(CPLAtof(pszObjValue));
203         else if (cmpStr("R", pszTagName) == 0) {
204         // radius = CPLAtof(pszObjValue);
205         }
206     }
207     CPLFree(pszObjValue);
208     XMLString::release(&pszTagName);
209     arcElem = dynamic_cast<DOMElement*>(arcElem->getNextSibling());
210   }
211   arc->addPoint(ptStart);
212   arc->addPoint(ptOnArc);
213   arc->addPoint(ptEnd);
214   delete ptStart;
215   delete ptOnArc;
216   delete ptEnd;
217   return arc;
218 }
219 
getPolyline(DOMElement * elem)220 static OGRCompoundCurve *getPolyline(DOMElement *elem) {
221   // elem -> POLYLINE
222   OGRCompoundCurve *ogrCurve = new OGRCompoundCurve();
223   OGRLineString *ls = new OGRLineString();
224 
225   DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
226   while (lineElem != nullptr) {
227     char* pszTagName = XMLString::transcode(lineElem->getTagName());
228     if (cmpStr(ILI2_COORD, pszTagName) == 0)
229     {
230       OGRPoint* poPoint = getPoint(lineElem);
231       if( poPoint )
232       {
233         ls->addPoint(poPoint);
234         delete poPoint;
235       }
236     }
237     else if (cmpStr(ILI2_ARC, pszTagName) == 0) {
238       //Finish line and start arc
239       if (ls->getNumPoints() > 1) {
240         ogrCurve->addCurveDirectly(ls);
241         ls = new OGRLineString();
242       } else {
243         ls->empty();
244       }
245       OGRCircularString *arc = new OGRCircularString();
246       // end point
247       OGRPoint *ptEnd = new OGRPoint();
248       // point on the arc
249       OGRPoint *ptOnArc = new OGRPoint();
250       // radius
251       // double radius = 0;
252 
253       DOMElement *arcElem = dynamic_cast<DOMElement *>(lineElem->getFirstChild());
254       while (arcElem != nullptr) {
255         char* pszTagName2 = XMLString::transcode(arcElem->getTagName());
256         char* pszObjValue = getObjValue(arcElem);
257         if( pszObjValue )
258         {
259             if (cmpStr("C1", pszTagName2) == 0)
260             ptEnd->setX(CPLAtof(pszObjValue));
261             else if (cmpStr("C2", pszTagName2) == 0)
262             ptEnd->setY(CPLAtof(pszObjValue));
263             else if (cmpStr("C3", pszTagName2) == 0)
264             ptEnd->setZ(CPLAtof(pszObjValue));
265             else if (cmpStr("A1", pszTagName2) == 0)
266             ptOnArc->setX(CPLAtof(pszObjValue));
267             else if (cmpStr("A2", pszTagName2) == 0)
268             ptOnArc->setY(CPLAtof(pszObjValue));
269             else if (cmpStr("A3", pszTagName2) == 0)
270             ptOnArc->setZ(CPLAtof(pszObjValue));
271             else if (cmpStr("R", pszTagName2) == 0) {
272             // radius = CPLAtof(pszObjValue);
273             }
274         }
275         CPLFree(pszObjValue);
276         XMLString::release(&pszTagName2);
277 
278         arcElem = dynamic_cast<DOMElement *>(arcElem->getNextSibling());
279       }
280 
281       auto elemPrev = dynamic_cast<DOMElement *>(lineElem->getPreviousSibling());
282       if( elemPrev )
283       {
284         OGRPoint *ptStart = getPoint(elemPrev); // COORD or ARC
285         if( ptStart )
286             arc->addPoint(ptStart);
287         delete ptStart;
288       }
289       arc->addPoint(ptOnArc);
290       arc->addPoint(ptEnd);
291       ogrCurve->addCurveDirectly(arc);
292 
293       delete ptEnd;
294       delete ptOnArc;
295     } /* else { // TODO: StructureValue in Polyline not yet supported
296     } */
297     XMLString::release(&pszTagName);
298 
299     lineElem = dynamic_cast<DOMElement *>(lineElem->getNextSibling());
300   }
301 
302   if (ls->getNumPoints() > 1) {
303     ogrCurve->addCurveDirectly(ls);
304   }
305   else {
306     delete ls;
307   }
308   return ogrCurve;
309 }
310 
getBoundary(DOMElement * elem)311 static OGRCompoundCurve *getBoundary(DOMElement *elem) {
312 
313   DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
314   if (lineElem != nullptr)
315   {
316     char* pszTagName = XMLString::transcode(lineElem->getTagName());
317     if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
318     {
319       XMLString::release(&pszTagName);
320       return getPolyline(lineElem);
321     }
322     XMLString::release(&pszTagName);
323   }
324 
325   return new OGRCompoundCurve();
326 }
327 
getPolygon(DOMElement * elem)328 static OGRCurvePolygon *getPolygon(DOMElement *elem) {
329   OGRCurvePolygon *pg = new OGRCurvePolygon();
330 
331   DOMElement *boundaryElem = dynamic_cast<DOMElement *>(elem->getFirstChild()); // outer boundary
332   while (boundaryElem != nullptr) {
333     char* pszTagName = XMLString::transcode(boundaryElem->getTagName());
334     if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
335     {
336         OGRCompoundCurve* poCC = getBoundary(boundaryElem);
337         if( pg->addRingDirectly(poCC) != OGRERR_NONE )
338         {
339             delete poCC;
340         }
341     }
342     XMLString::release(&pszTagName);
343     boundaryElem = dynamic_cast<DOMElement *>(boundaryElem->getNextSibling()); // inner boundaries
344   }
345 
346   return pg;
347 }
348 
getGeometry(DOMElement * elem,int type)349 OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type) {
350   OGRGeometryCollection *gm = new OGRGeometryCollection();
351 
352   DOMElement *childElem = elem;
353   while (childElem != nullptr) {
354     char* pszTagName = XMLString::transcode(childElem->getTagName());
355     switch (type) {
356       case ILI2_COORD_TYPE :
357         if (cmpStr(ILI2_COORD, pszTagName) == 0)
358         {
359           delete gm;
360           XMLString::release(&pszTagName);
361           return getPoint(childElem);
362         }
363         break;
364       case ILI2_ARC_TYPE :
365         // is it possible here? It have to be a ARC or COORD before (getPreviousSibling)
366         if (cmpStr(ILI2_ARC, pszTagName) == 0)
367         {
368           delete gm;
369           XMLString::release(&pszTagName);
370           return getArc(childElem);
371         }
372         break;
373       case ILI2_POLYLINE_TYPE :
374         if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
375         {
376           delete gm;
377           XMLString::release(&pszTagName);
378           return getPolyline(childElem);
379         }
380         break;
381       case ILI2_BOUNDARY_TYPE :
382         if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
383         {
384           delete gm;
385           XMLString::release(&pszTagName);
386           return getPolyline(childElem);
387         }
388         break;
389       case ILI2_AREA_TYPE :
390         if ((cmpStr(ILI2_AREA, pszTagName) == 0) ||
391           (cmpStr(ILI2_SURFACE, pszTagName) == 0))
392         {
393           delete gm;
394           XMLString::release(&pszTagName);
395           return getPolygon(childElem);
396         }
397         break;
398       default :
399         if (type >= ILI2_GEOMCOLL_TYPE) {
400           int subType = getGeometryTypeOfElem(childElem); //????
401           OGRGeometry* poSubGeom = getGeometry(childElem, subType);
402           if( poSubGeom )
403             gm->addGeometryDirectly(poSubGeom);
404         }
405         break;
406     }
407     XMLString::release(&pszTagName);
408 
409     // GEOMCOLL
410     childElem = dynamic_cast<DOMElement *>(childElem->getNextSibling());
411   }
412 
413   return gm;
414 }
415 
ReadModel(ImdReader * poImdReader,const char * modelFilename)416 int ILI2Reader::ReadModel(ImdReader *poImdReader, const char *modelFilename) {
417   poImdReader->ReadModel(modelFilename);
418   for (FeatureDefnInfos::const_iterator it = poImdReader->featureDefnInfos.begin(); it != poImdReader->featureDefnInfos.end(); ++it)
419   {
420     OGRLayer* layer = new OGRILI2Layer(it->GetTableDefnRef(), it->poGeomFieldInfos, nullptr);
421     m_listLayer.push_back(layer);
422   }
423   return 0;
424 }
425 
426 //Detect field name of value element
fieldName(DOMElement * elem)427 static char* fieldName(DOMElement* elem) {
428   DOMNode *node = elem;
429   if (getGeometryTypeOfElem(elem))
430   {
431     int depth = 0; // Depth of value elem node
432     for (node = elem; node; node = node->getParentNode()) ++depth;
433     //Field name is on level 4
434     node = elem;
435     for (int d = 0; d<depth-4; ++d) node = node->getParentNode();
436   }
437   if( node == nullptr )
438   {
439       CPLError(CE_Failure, CPLE_AssertionFailed, "node == NULL");
440       return CPLStrdup("***bug***");
441   }
442   return CPLStrdup(transcode(node->getNodeName()));
443 }
444 
setFieldDefn(OGRFeatureDefn * featureDef,DOMElement * elem)445 void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement* elem) {
446   int type = 0;
447   //recursively search children
448   for (DOMNode *childNode = elem->getFirstChild();
449         type == 0 && childNode && childNode->getNodeType() == DOMNode::ELEMENT_NODE;
450         childNode = childNode->getNextSibling()) {
451     DOMElement* childElem = dynamic_cast<DOMElement*>(childNode);
452     CPLAssert(childElem);
453     type = getGeometryTypeOfElem(childElem);
454     if (type == 0) {
455       if (childElem->getFirstChild() && childElem->getFirstChild()->getNodeType() == DOMNode::ELEMENT_NODE) {
456         setFieldDefn(featureDef, childElem);
457       } else {
458         char *fName = fieldName(childElem);
459         if (featureDef->GetFieldIndex(fName) == -1) {
460           CPLDebug( "OGR_ILI", "AddFieldDefn: %s",fName );
461           OGRFieldDefn oFieldDefn(fName, OFTString);
462           featureDef->AddFieldDefn(&oFieldDefn);
463         }
464         CPLFree(fName);
465       }
466     }
467   }
468 }
469 
SetFieldValues(OGRFeature * feature,DOMElement * elem)470 void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement* elem) {
471   int type = 0;
472   //recursively search children
473   for (DOMNode *childNode = elem->getFirstChild();
474         type == 0 && childNode && childNode->getNodeType() == DOMNode::ELEMENT_NODE;
475         childNode = childNode->getNextSibling()) {
476     DOMElement* childElem = dynamic_cast<DOMElement*>(childNode);
477     CPLAssert(childElem);
478     type = getGeometryTypeOfElem(childElem);
479     if (type == 0) {
480       if (childElem->getFirstChild() && childElem->getFirstChild()->getNodeType() == DOMNode::ELEMENT_NODE) {
481         SetFieldValues(feature, childElem);
482       } else {
483         char *fName = fieldName(childElem);
484         int fIndex = feature->GetFieldIndex(fName);
485         if (fIndex != -1) {
486           char * objVal = getObjValue(childElem);
487           if (objVal == nullptr)
488             objVal = getREFValue(childElem); // only to try
489           feature->SetField(fIndex, objVal);
490           CPLFree(objVal);
491         } else {
492           CPLDebug( "OGR_ILI","Attribute '%s' not found", fName);
493           m_missAttrs.push_back(fName);
494         }
495         CPLFree(fName);
496       }
497     } else {
498       char *fName = fieldName(childElem);
499       int fIndex = feature->GetGeomFieldIndex(fName);
500       OGRGeometry *geom = getGeometry(childElem, type);
501       if( geom )
502       {
503         if (fIndex == -1) { // Unknown model
504             feature->SetGeometryDirectly(geom);
505         } else {
506             OGRwkbGeometryType geomType = feature->GetGeomFieldDefnRef(fIndex)->GetType();
507             if (geomType == wkbMultiLineString || geomType == wkbPolygon) {
508             feature->SetGeomFieldDirectly(fIndex, geom->getLinearGeometry());
509             delete geom;
510             } else {
511             feature->SetGeomFieldDirectly(fIndex, geom);
512             }
513         }
514       }
515       CPLFree(fName);
516     }
517   }
518 }
519 
520 //
521 // ILI2Reader
522 //
~IILI2Reader()523 IILI2Reader::~IILI2Reader() {
524 }
525 
ILI2Reader()526 ILI2Reader::ILI2Reader() :
527     m_pszFilename(nullptr),
528     m_poILI2Handler(nullptr),
529     m_poSAXReader(nullptr),
530     m_bReadStarted(FALSE),
531     m_bXercesInitialized(false)
532 {
533     SetupParser();
534 }
535 
~ILI2Reader()536 ILI2Reader::~ILI2Reader() {
537     CPLFree( m_pszFilename );
538 
539     CleanupParser();
540 
541     if( m_bXercesInitialized )
542         OGRDeinitializeXerces();
543 
544     list<OGRLayer *>::const_iterator layerIt = m_listLayer.begin();
545     while (layerIt != m_listLayer.end()) {
546         OGRILI2Layer *tmpLayer = (OGRILI2Layer *)*layerIt;
547         delete tmpLayer;
548         ++layerIt;
549     }
550 }
551 
SetSourceFile(const char * pszFilename)552 void ILI2Reader::SetSourceFile( const char *pszFilename ) {
553     CPLFree( m_pszFilename );
554     m_pszFilename = CPLStrdup( pszFilename );
555 }
556 
SetupParser()557 int ILI2Reader::SetupParser() {
558 
559     if( !m_bXercesInitialized )
560     {
561         if( !OGRInitializeXerces() )
562             return FALSE;
563         m_bXercesInitialized = true;
564     }
565 
566     // Cleanup any old parser.
567     if( m_poSAXReader != nullptr )
568         CleanupParser();
569 
570     // Create and initialize parser.
571     m_poSAXReader = XMLReaderFactory::createXMLReader();
572 
573     m_poILI2Handler = new ILI2Handler( this );
574 
575     m_poSAXReader->setContentHandler( m_poILI2Handler );
576     m_poSAXReader->setErrorHandler( m_poILI2Handler );
577     m_poSAXReader->setLexicalHandler( m_poILI2Handler );
578     m_poSAXReader->setEntityResolver( m_poILI2Handler );
579     m_poSAXReader->setDTDHandler( m_poILI2Handler );
580 
581 /* No Validation
582 #if (OGR_ILI2_VALIDATION)
583     m_poSAXReader->setFeature(
584         XMLString::transcode("http://xml.org/sax/features/validation"), true);
585     m_poSAXReader->setFeature(
586         XMLString::transcode("http://xml.org/sax/features/namespaces"), true);
587 
588     m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true );
589     m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true );
590 
591 //    m_poSAXReader->setDoSchema(true);
592 //    m_poSAXReader->setValidationSchemaFullChecking(true);
593 #else
594 */
595     XMLCh *tmpCh = XMLString::transcode("http://xml.org/sax/features/validation");
596     m_poSAXReader->setFeature(tmpCh, false);
597     XMLString::release(&tmpCh);
598     tmpCh = XMLString::transcode("http://xml.org/sax/features/namespaces");
599     m_poSAXReader->setFeature(tmpCh, false);
600     XMLString::release(&tmpCh);
601 //#endif
602 
603     m_bReadStarted = FALSE;
604 
605     return TRUE;
606 }
607 
CleanupParser()608 void ILI2Reader::CleanupParser() {
609     if( m_poSAXReader == nullptr )
610         return;
611 
612     delete m_poSAXReader;
613     m_poSAXReader = nullptr;
614 
615     delete m_poILI2Handler;
616     m_poILI2Handler = nullptr;
617 
618     m_bReadStarted = FALSE;
619 }
620 
SaveClasses(const char * pszFile=nullptr)621 int ILI2Reader::SaveClasses( const char *pszFile = nullptr ) {
622 
623     // Add logic later to determine reasonable default schema file.
624     if( pszFile == nullptr )
625         return FALSE;
626 
627     VSILFILE* fp = VSIFOpenL(pszFile, "rb");
628     if( fp == nullptr )
629         return FALSE;
630 
631     InputSource* is = OGRCreateXercesInputSource(fp);
632 
633     // parse and create layers and features
634     try
635     {
636         CPLDebug( "OGR_ILI", "Parsing %s", pszFile);
637         m_poSAXReader->parse(*is);
638         VSIFCloseL(fp);
639         OGRDestroyXercesInputSource(is);
640     }
641     catch (const DOMException& toCatch)
642     {
643         // Can happen with createElement() in ILI2Handler::startElement()
644         CPLError( CE_Failure, CPLE_AppDefined,
645                   "DOMException: %s\n",
646                   transcode(toCatch.getMessage()).c_str());
647         VSIFCloseL(fp);
648         OGRDestroyXercesInputSource(is);
649         return FALSE;
650     }
651     catch (const SAXException& toCatch)
652     {
653         CPLError( CE_Failure, CPLE_AppDefined,
654                   "Parsing failed: %s\n",
655                   transcode(toCatch.getMessage()).c_str());
656         VSIFCloseL(fp);
657         OGRDestroyXercesInputSource(is);
658         return FALSE;
659     }
660 
661   if (!m_missAttrs.empty()) {
662     m_missAttrs.sort();
663     m_missAttrs.unique();
664     string attrs = "";
665     list<string>::const_iterator it;
666     for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it)
667       attrs += *it + ", ";
668 
669     CPLError( CE_Warning, CPLE_NotSupported,
670               "Failed to add new definition to existing layers, attributes not saved: %s", attrs.c_str() );
671   }
672 
673     return TRUE;
674 }
675 
GetLayers()676 list<OGRLayer *> ILI2Reader::GetLayers() {
677   return m_listLayer;
678 }
679 
GetLayerCount()680 int ILI2Reader::GetLayerCount() {
681   return static_cast<int>(m_listLayer.size());
682 }
683 
GetLayer(const char * pszName)684 OGRLayer* ILI2Reader::GetLayer(const char* pszName) {
685   for (list<OGRLayer *>::reverse_iterator layerIt = m_listLayer.rbegin();
686        layerIt != m_listLayer.rend();
687        ++layerIt) {
688     OGRFeatureDefn *fDef = (*layerIt)->GetLayerDefn();
689     if (cmpStr(fDef->GetName(), pszName) == 0) {
690       return *layerIt;
691     }
692   }
693   return nullptr;
694 }
695 
AddFeature(DOMElement * elem)696 int ILI2Reader::AddFeature(DOMElement *elem) {
697   CPLString osName(transcode(elem->getTagName()));
698   //CPLDebug( "OGR_ILI", "Reading layer: %s", osName.c_str() );
699 
700   // test if this layer exist
701   OGRILI2Layer* curLayer = dynamic_cast<OGRILI2Layer*>(GetLayer(osName));
702   bool newLayer = (curLayer == nullptr);
703 
704   // add a layer
705   if (newLayer) {
706     CPLDebug( "OGR_ILI", "Adding layer: %s", osName.c_str() );
707     OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(osName);
708     poFeatureDefn->SetGeomType( wkbUnknown );
709     GeomFieldInfos oGeomFieldInfos;
710     curLayer = new OGRILI2Layer(poFeatureDefn, oGeomFieldInfos, nullptr);
711     m_listLayer.push_back(curLayer);
712   }
713 
714   // the feature and field definition
715   OGRFeatureDefn *featureDef = curLayer->GetLayerDefn();
716   if (newLayer) {
717     // add TID field
718     OGRFieldDefn ofieldDefn (ILI2_TID, OFTString);
719     featureDef->AddFieldDefn(&ofieldDefn);
720 
721     setFieldDefn(featureDef, elem);
722   }
723 
724   // add the features
725   OGRFeature *feature = new OGRFeature(featureDef);
726 
727   // assign TID
728   int fIndex = feature->GetFieldIndex(ILI2_TID);
729   if (fIndex != -1) {
730       feature->SetField(fIndex, transcode(elem->getAttribute(xmlch_ILI2_TID)).c_str());
731   } else {
732       CPLDebug( "OGR_ILI","'%s' not found", ILI2_TID);
733   }
734 
735   SetFieldValues(feature, elem);
736   curLayer->AddFeature(feature);
737 
738   return 0;
739 }
740 
CreateILI2Reader()741 IILI2Reader *CreateILI2Reader() {
742     return new ILI2Reader();
743 }
744 
DestroyILI2Reader(IILI2Reader * reader)745 void DestroyILI2Reader(IILI2Reader* reader)
746 {
747     if (reader)
748         delete reader;
749 }
750