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