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