LibOFX

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 "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 }