ofx_sgml.cpp
Go to the documentation of this file.

    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 }