LibOFX
|
00001 /*************************************************************************** 00002 ofx_sgml.cpp 00003 ------------------- 00004 copyright : (C) 2002 by Benoit Grégoire 00005 email : benoitg@coeus.ca 00006 ***************************************************************************/ 00012 /*************************************************************************** 00013 * * 00014 * This program is free software; you can redistribute it and/or modify * 00015 * it under the terms of the GNU General Public License as published by * 00016 * the Free Software Foundation; either version 2 of the License, or * 00017 * (at your option) any later version. * 00018 * * 00019 ***************************************************************************/ 00020 00021 #ifdef HAVE_CONFIG_H 00022 #include <config.h> 00023 #endif 00024 00025 #include <iostream> 00026 #include <stdlib.h> 00027 #include <string> 00028 #include <cassert> 00029 #include "ParserEventGeneratorKit.h" 00030 #include "libofx.h" 00031 #include "ofx_utilities.hh" 00032 #include "messages.hh" 00033 #include "ofx_containers.hh" 00034 #include "ofx_sgml.hh" 00035 00036 using namespace std; 00037 00038 OfxMainContainer * MainContainer = NULL; 00039 extern SGMLApplication::OpenEntityPtr entity_ptr; 00040 extern SGMLApplication::Position position; 00041 00042 00045 class OFXApplication : public SGMLApplication 00046 { 00047 private: 00048 OfxGenericContainer *curr_container_element; 00049 OfxGenericContainer *tmp_container_element; 00050 bool is_data_element; 00051 string incoming_data; 00052 LibofxContext * libofx_context; 00053 00054 public: 00055 00056 OFXApplication (LibofxContext * p_libofx_context) 00057 { 00058 MainContainer = NULL; 00059 curr_container_element = NULL; 00060 is_data_element = false; 00061 libofx_context = p_libofx_context; 00062 } 00063 ~OFXApplication() 00064 { 00065 message_out(DEBUG, "Entering the OFXApplication's destructor"); 00066 } 00067 00072 void startElement (const StartElementEvent & event) 00073 { 00074 string identifier; 00075 CharStringtostring (event.gi, identifier); 00076 message_out(PARSER, "startElement event received from OpenSP for element " + identifier); 00077 00078 position = event.pos; 00079 00080 switch (event.contentType) 00081 { 00082 case StartElementEvent::empty: 00083 message_out(ERROR, "StartElementEvent::empty\n"); 00084 break; 00085 case StartElementEvent::cdata: 00086 message_out(ERROR, "StartElementEvent::cdata\n"); 00087 break; 00088 case StartElementEvent::rcdata: 00089 message_out(ERROR, "StartElementEvent::rcdata\n"); 00090 break; 00091 case StartElementEvent::mixed: 00092 message_out(PARSER, "StartElementEvent::mixed"); 00093 is_data_element = true; 00094 break; 00095 case StartElementEvent::element: 00096 message_out(PARSER, "StartElementEvent::element"); 00097 is_data_element = false; 00098 break; 00099 default: 00100 message_out(ERROR, "Unknow SGML content type?!?!?!? OpenSP interface changed?"); 00101 } 00102 00103 if (is_data_element == false) 00104 { 00105 /*------- The following are OFX entities ---------------*/ 00106 00107 if (identifier == "OFX") 00108 { 00109 message_out (PARSER, "Element " + identifier + " found"); 00110 MainContainer = new OfxMainContainer (libofx_context, curr_container_element, identifier); 00111 curr_container_element = MainContainer; 00112 } 00113 else if (identifier == "STATUS") 00114 { 00115 message_out (PARSER, "Element " + identifier + " found"); 00116 curr_container_element = new OfxStatusContainer (libofx_context, curr_container_element, identifier); 00117 } 00118 else if (identifier == "STMTRS" || 00119 identifier == "CCSTMTRS" || 00120 identifier == "INVSTMTRS") 00121 { 00122 message_out (PARSER, "Element " + identifier + " found"); 00123 curr_container_element = new OfxStatementContainer (libofx_context, curr_container_element, identifier); 00124 } 00125 else if (identifier == "BANKTRANLIST") 00126 { 00127 message_out (PARSER, "Element " + identifier + " found"); 00128 //BANKTRANLIST ignored, we will process it's attributes directly inside the STATEMENT, 00129 if (curr_container_element->type != "STATEMENT") 00130 { 00131 message_out(ERROR, "Element " + identifier + " found while not inside a STATEMENT container"); 00132 } 00133 else 00134 { 00135 curr_container_element = new OfxPushUpContainer (libofx_context, curr_container_element, identifier); 00136 } 00137 } 00138 else if (identifier == "STMTTRN") 00139 { 00140 message_out (PARSER, "Element " + identifier + " found"); 00141 curr_container_element = new OfxBankTransactionContainer (libofx_context, curr_container_element, identifier); 00142 } 00143 else if (identifier == "BUYDEBT" || 00144 identifier == "BUYMF" || 00145 identifier == "BUYOPT" || 00146 identifier == "BUYOTHER" || 00147 identifier == "BUYSTOCK" || 00148 identifier == "CLOSUREOPT" || 00149 identifier == "INCOME" || 00150 identifier == "INVEXPENSE" || 00151 identifier == "JRNLFUND" || 00152 identifier == "JRNLSEC" || 00153 identifier == "MARGININTEREST" || 00154 identifier == "REINVEST" || 00155 identifier == "RETOFCAP" || 00156 identifier == "SELLDEBT" || 00157 identifier == "SELLMF" || 00158 identifier == "SELLOPT" || 00159 identifier == "SELLOTHER" || 00160 identifier == "SELLSTOCK" || 00161 identifier == "SPLIT" || 00162 identifier == "TRANSFER" ) 00163 { 00164 message_out (PARSER, "Element " + identifier + " found"); 00165 curr_container_element = new OfxInvestmentTransactionContainer (libofx_context, curr_container_element, identifier); 00166 } 00167 /*The following is a list of OFX elements whose attributes will be processed by the parent container*/ 00168 else if (identifier == "INVBUY" || 00169 identifier == "INVSELL" || 00170 identifier == "INVTRAN" || 00171 identifier == "SECID") 00172 { 00173 message_out (PARSER, "Element " + identifier + " found"); 00174 curr_container_element = new OfxPushUpContainer (libofx_context, curr_container_element, identifier); 00175 } 00176 00177 /* The different types of accounts */ 00178 else if (identifier == "BANKACCTFROM" || identifier == "CCACCTFROM" || identifier == "INVACCTFROM") 00179 { 00180 message_out (PARSER, "Element " + identifier + " found"); 00181 curr_container_element = new OfxAccountContainer (libofx_context, curr_container_element, identifier); 00182 } 00183 else if (identifier == "SECINFO") 00184 { 00185 message_out (PARSER, "Element " + identifier + " found"); 00186 curr_container_element = new OfxSecurityContainer (libofx_context, curr_container_element, identifier); 00187 } 00188 /* The different types of balances */ 00189 else if (identifier == "LEDGERBAL" || identifier == "AVAILBAL") 00190 { 00191 message_out (PARSER, "Element " + identifier + " found"); 00192 curr_container_element = new OfxBalanceContainer (libofx_context, curr_container_element, identifier); 00193 } 00194 else 00195 { 00196 /* We dont know this OFX element, so we create a dummy container */ 00197 curr_container_element = new OfxDummyContainer(libofx_context, curr_container_element, identifier); 00198 } 00199 } 00200 else 00201 { 00202 /* The element was a data element. OpenSP will call one or several data() callback with the data */ 00203 message_out (PARSER, "Data element " + identifier + " found"); 00204 /* There is a bug in OpenSP 1.3.4, which won't send endElement Event for some elements, and will instead send an error like "document type does not allow element "MESSAGE" here". Incoming_data should be empty in such a case, but it will not be if the endElement event was skiped. So we empty it, so at least the last element has a chance of having valid data */ 00205 if (incoming_data != "") 00206 { 00207 message_out (ERROR, "startElement: incoming_data should be empty! You are probably using OpenSP <= 1.3.4. The folowing data was lost: " + incoming_data ); 00208 incoming_data.assign (""); 00209 } 00210 } 00211 } 00212 00217 void endElement (const EndElementEvent & event) 00218 { 00219 string identifier; 00220 bool end_element_for_data_element; 00221 00222 CharStringtostring (event.gi, identifier); 00223 end_element_for_data_element = is_data_element; 00224 message_out(PARSER, "endElement event received from OpenSP for element " + identifier); 00225 00226 position = event.pos; 00227 if (curr_container_element == NULL) 00228 { 00229 message_out (ERROR, "Tried to close a " + identifier + " without a open element (NULL pointer)"); 00230 incoming_data.assign (""); 00231 } 00232 else //curr_container_element != NULL 00233 { 00234 if (end_element_for_data_element == true) 00235 { 00236 incoming_data = strip_whitespace(incoming_data); 00237 00238 curr_container_element->add_attribute (identifier, incoming_data); 00239 message_out (PARSER, "endElement: Added data '" + incoming_data + "' from " + identifier + " to " + curr_container_element->type + " container_element"); 00240 incoming_data.assign (""); 00241 is_data_element = false; 00242 } 00243 else 00244 { 00245 if (identifier == curr_container_element->tag_identifier) 00246 { 00247 if (incoming_data != "") 00248 { 00249 message_out(ERROR, "End tag for non data element " + identifier + ", incoming data should be empty but contains: " + incoming_data + " DATA HAS BEEN LOST SOMEWHERE!"); 00250 } 00251 00252 if (identifier == "OFX") 00253 { 00254 /* The main container is a special case */ 00255 tmp_container_element = curr_container_element; 00256 curr_container_element = curr_container_element->getparent (); 00257 if (curr_container_element == NULL) 00258 { 00259 //Defensive coding, this isn't supposed to happen 00260 curr_container_element = tmp_container_element; 00261 } 00262 if (MainContainer != NULL) 00263 { 00264 MainContainer->gen_event(); 00265 delete MainContainer; 00266 MainContainer = NULL; 00267 message_out (DEBUG, "Element " + identifier + " closed, MainContainer destroyed"); 00268 } 00269 else 00270 { 00271 message_out (DEBUG, "Element " + identifier + " closed, but there was no MainContainer to destroy (probably a malformed file)!"); 00272 } 00273 } 00274 else 00275 { 00276 tmp_container_element = curr_container_element; 00277 curr_container_element = curr_container_element->getparent (); 00278 if (MainContainer != NULL) 00279 { 00280 tmp_container_element->add_to_main_tree(); 00281 message_out (PARSER, "Element " + identifier + " closed, object added to MainContainer"); 00282 } 00283 else 00284 { 00285 message_out (ERROR, "MainContainer is NULL trying to add element " + identifier); 00286 } 00287 } 00288 } 00289 else 00290 { 00291 message_out (ERROR, "Tried to close a " + identifier + " but a " + curr_container_element->type + " is currently open."); 00292 } 00293 } 00294 } 00295 } 00296 00301 void data (const DataEvent & event) 00302 { 00303 string tmp; 00304 position = event.pos; 00305 AppendCharStringtostring (event.data, incoming_data); 00306 message_out(PARSER, "data event received from OpenSP, incoming_data is now: " + incoming_data); 00307 } 00308 00313 void error (const ErrorEvent & event) 00314 { 00315 string message; 00316 string string_buf; 00317 OfxMsgType error_type = ERROR; 00318 00319 position = event.pos; 00320 message = message + "OpenSP parser: "; 00321 switch (event.type) 00322 { 00323 case SGMLApplication::ErrorEvent::quantity: 00324 message = message + "quantity (Exceeding a quantity limit):"; 00325 error_type = ERROR; 00326 break; 00327 case SGMLApplication::ErrorEvent::idref: 00328 message = message + "idref (An IDREF to a non-existent ID):"; 00329 error_type = ERROR; 00330 break; 00331 case SGMLApplication::ErrorEvent::capacity: 00332 message = message + "capacity (Exceeding a capacity limit):"; 00333 error_type = ERROR; 00334 break; 00335 case SGMLApplication::ErrorEvent::otherError: 00336 message = message + "otherError (misc parse error):"; 00337 error_type = ERROR; 00338 break; 00339 case SGMLApplication::ErrorEvent::warning: 00340 message = message + "warning (Not actually an error.):"; 00341 error_type = WARNING; 00342 break; 00343 case SGMLApplication::ErrorEvent::info: 00344 message = message + "info (An informationnal message. Not actually an error):"; 00345 error_type = INFO; 00346 break; 00347 default: 00348 message = message + "OpenSP sent an unknown error to LibOFX (You probably have a newer version of OpenSP):"; 00349 } 00350 message = message + "\n" + CharStringtostring (event.message, string_buf); 00351 message_out (error_type, message); 00352 } 00353 00358 void openEntityChange (const OpenEntityPtr & para_entity_ptr) 00359 { 00360 message_out(DEBUG, "openEntityChange()\n"); 00361 entity_ptr = para_entity_ptr; 00362 00363 }; 00364 00365 private: 00366 }; 00367 00371 int ofx_proc_sgml(LibofxContext * libofx_context, int argc, char * const* argv) 00372 { 00373 message_out(DEBUG, "Begin ofx_proc_sgml()"); 00374 assert(argc >= 3); 00375 message_out(DEBUG, argv[0]); 00376 message_out(DEBUG, argv[1]); 00377 message_out(DEBUG, argv[2]); 00378 00379 ParserEventGeneratorKit parserKit; 00380 parserKit.setOption (ParserEventGeneratorKit::showOpenEntities); 00381 EventGenerator *egp = parserKit.makeEventGenerator (argc, argv); 00382 egp->inhibitMessages (true); /* Error output is handled by libofx not OpenSP */ 00383 OFXApplication *app = new OFXApplication(libofx_context); 00384 unsigned nErrors = egp->run (*app); /* Begin parsing */ 00385 delete egp; //Note that this is where bug is triggered 00386 return nErrors > 0; 00387 }