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 curr_container_element = NULL; 00268 message_out (DEBUG, "Element " + identifier + " closed, MainContainer destroyed"); 00269 } 00270 else 00271 { 00272 message_out (DEBUG, "Element " + identifier + " closed, but there was no MainContainer to destroy (probably a malformed file)!"); 00273 } 00274 } 00275 else 00276 { 00277 tmp_container_element = curr_container_element; 00278 curr_container_element = curr_container_element->getparent (); 00279 if (MainContainer != NULL) 00280 { 00281 tmp_container_element->add_to_main_tree(); 00282 message_out (PARSER, "Element " + identifier + " closed, object added to MainContainer"); 00283 } 00284 else 00285 { 00286 message_out (ERROR, "MainContainer is NULL trying to add element " + identifier); 00287 } 00288 } 00289 } 00290 else 00291 { 00292 message_out (ERROR, "Tried to close a " + identifier + " but a " + curr_container_element->type + " is currently open."); 00293 } 00294 } 00295 } 00296 } 00297 00302 void data (const DataEvent & event) 00303 { 00304 string tmp; 00305 position = event.pos; 00306 AppendCharStringtostring (event.data, incoming_data); 00307 message_out(PARSER, "data event received from OpenSP, incoming_data is now: " + incoming_data); 00308 } 00309 00314 void error (const ErrorEvent & event) 00315 { 00316 string message; 00317 string string_buf; 00318 OfxMsgType error_type = ERROR; 00319 00320 position = event.pos; 00321 message = message + "OpenSP parser: "; 00322 switch (event.type) 00323 { 00324 case SGMLApplication::ErrorEvent::quantity: 00325 message = message + "quantity (Exceeding a quantity limit):"; 00326 error_type = ERROR; 00327 break; 00328 case SGMLApplication::ErrorEvent::idref: 00329 message = message + "idref (An IDREF to a non-existent ID):"; 00330 error_type = ERROR; 00331 break; 00332 case SGMLApplication::ErrorEvent::capacity: 00333 message = message + "capacity (Exceeding a capacity limit):"; 00334 error_type = ERROR; 00335 break; 00336 case SGMLApplication::ErrorEvent::otherError: 00337 message = message + "otherError (misc parse error):"; 00338 error_type = ERROR; 00339 break; 00340 case SGMLApplication::ErrorEvent::warning: 00341 message = message + "warning (Not actually an error.):"; 00342 error_type = WARNING; 00343 break; 00344 case SGMLApplication::ErrorEvent::info: 00345 message = message + "info (An informationnal message. Not actually an error):"; 00346 error_type = INFO; 00347 break; 00348 default: 00349 message = message + "OpenSP sent an unknown error to LibOFX (You probably have a newer version of OpenSP):"; 00350 } 00351 message = message + "\n" + CharStringtostring (event.message, string_buf); 00352 message_out (error_type, message); 00353 } 00354 00359 void openEntityChange (const OpenEntityPtr & para_entity_ptr) 00360 { 00361 message_out(DEBUG, "openEntityChange()\n"); 00362 entity_ptr = para_entity_ptr; 00363 00364 }; 00365 00366 private: 00367 }; 00368 00372 int ofx_proc_sgml(LibofxContext * libofx_context, int argc, char * const* argv) 00373 { 00374 message_out(DEBUG, "Begin ofx_proc_sgml()"); 00375 assert(argc >= 3); 00376 message_out(DEBUG, argv[0]); 00377 message_out(DEBUG, argv[1]); 00378 message_out(DEBUG, argv[2]); 00379 00380 ParserEventGeneratorKit parserKit; 00381 parserKit.setOption (ParserEventGeneratorKit::showOpenEntities); 00382 EventGenerator *egp = parserKit.makeEventGenerator (argc, argv); 00383 egp->inhibitMessages (true); /* Error output is handled by libofx not OpenSP */ 00384 OFXApplication *app = new OFXApplication(libofx_context); 00385 unsigned nErrors = egp->run (*app); /* Begin parsing */ 00386 delete egp; //Note that this is where bug is triggered 00387 return nErrors > 0; 00388 }