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