1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 
19 #include "itkDOMNodeXMLReader.h"
20 #include "expat.h"
21 
22 #include <fstream>
23 
24 namespace itk
25 {
26 
27 /**
28  * The following three functions are called by the expat XML parser during the parsing process,
29  * and the calls are forwarded to the callback functions of DOMNodeXMLReader.
30  */
31 
itkXMLParserStartElement(void * parser,const char * name,const char ** atts)32 static void itkXMLParserStartElement( void* parser, const char* name, const char** atts )
33 {
34   // Begin element handler that is registered with the XML_Parser.
35   // This just casts the user data to a itkXMLParser and calls
36   // StartElement.
37   static_cast<DOMNodeXMLReader*>(parser)->StartElement( name, atts );
38 }
39 
itkXMLParserEndElement(void * parser,const char * name)40 static void itkXMLParserEndElement( void* parser, const char* name )
41 {
42   // End element handler that is registered with the XML_Parser.  This
43   // just casts the user data to a itkXMLParser and calls EndElement.
44   static_cast<DOMNodeXMLReader*>(parser)->EndElement( name );
45 }
46 
itkXMLParserCharacterDataHandler(void * parser,const char * data,int length)47 static void itkXMLParserCharacterDataHandler( void* parser, const char* data, int length )
48 {
49   // Character data handler that is registered with the XML_Parser.
50   // This just casts the user data to a itkXMLParser and calls
51   // CharacterDataHandler.
52   static_cast<DOMNodeXMLReader*>(parser)->CharacterDataHandler( data, length );
53 }
54 
DOMNodeXMLReader()55 DOMNodeXMLReader::DOMNodeXMLReader()
56 {
57 }
58 
59 /**
60  * Function called by Update() or end-users to generate the output DOM object
61  * from an input stream such as file, string, etc.
62  */
63 void
Update(std::istream & is)64 DOMNodeXMLReader::Update( std::istream& is )
65 {
66   if ( m_DOMNodeXML.IsNull() )
67     {
68     OutputType::Pointer temp = OutputType::New();
69     this->SetDOMNodeXML( temp );
70     }
71   m_DOMNodeXML->RemoveAllAttributesAndChildren();
72   this->m_Context = nullptr;
73 
74   is >> std::noskipws;
75   std::string s;
76   while ( true )
77     {
78     char c = 0;
79     is >> c;
80     if ( !is.good() )
81       {
82       break;
83       }
84     s.append( 1, c );
85     }
86 
87   XML_Parser parser = XML_ParserCreate( nullptr );
88   XML_SetElementHandler( parser, &itkXMLParserStartElement, &itkXMLParserEndElement );
89   XML_SetCharacterDataHandler( parser, &itkXMLParserCharacterDataHandler );
90   XML_SetUserData( parser, this );
91 
92   bool ok = XML_Parse( parser, s.data(), static_cast<int>( s.size() ), false );
93   if ( !ok )
94     {
95     ExceptionObject e( __FILE__, __LINE__ );
96     std::string message( XML_ErrorString(XML_GetErrorCode(parser)) );
97     e.SetDescription( message.c_str() );
98     throw e;
99     }
100 
101   XML_ParserFree( parser );
102 }
103 
104 /**
105  * Function called by end-users to generate the output DOM object from the input XML file.
106  */
107 void
Update()108 DOMNodeXMLReader::Update()
109 {
110   std::ifstream is( this->m_FileName.c_str() );
111   if ( !is.is_open() )
112     {
113     itkExceptionMacro( "failed openning the input XML file" );
114     }
115 
116   this->Update( is );
117 
118   is.close();
119 }
120 
121 /** Callback function -- called from XML parser with start-of-element
122  * information.
123  */
124 void
StartElement(const char * name,const char ** atts)125 DOMNodeXMLReader::StartElement( const char* name, const char** atts )
126 {
127   OutputType* node = nullptr;
128   if ( this->m_Context )
129     {
130     OutputPointer node1 = OutputType::New();
131     node = (OutputType*)node1;
132     this->m_Context->AddChildAtEnd( node );
133     }
134   else
135     {
136     node = this->m_DOMNodeXML;
137     }
138 
139   node->SetName( name );
140 
141   size_t i = 0;
142   while ( atts[i] )
143     {
144     std::string key( atts[i++] );
145     std::string value( atts[i++] );
146     if ( StringTools::MatchWith(key,"id") )
147       {
148       node->SetID( value );
149       }
150     else
151       {
152       node->SetAttribute( key, value );
153       }
154     }
155 
156   this->m_Context = node;
157 }
158 
159 /** Callback function -- called from XML parser when ending tag
160  * encountered.
161  */
162 void
EndElement(const char * name)163 DOMNodeXMLReader::EndElement( const char* name )
164 {
165   if ( this->m_Context->GetName() != name )
166     {
167     itkExceptionMacro( "start/end tag names mismatch" );
168     }
169 
170   this->m_Context = this->m_Context->GetParent();
171 }
172 
173 /** Callback function -- called from XML parser with the character data
174  * for an XML element.
175  */
176 void
CharacterDataHandler(const char * text,int len)177 DOMNodeXMLReader::CharacterDataHandler( const char* text, int len )
178 {
179   std::string s( text, len );
180 
181   StringTools::Trim( s );
182   if ( s.empty() )
183     {
184     return;
185     }
186 
187   this->m_Context->AddTextChildAtEnd( s );
188 }
189 
190 } // namespace itk
191