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 <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               curr_container_element = NULL;
00268               message_out (DEBUG, "Element " + identifier + " closed, MainContainer destroyed");
00269             }
00270             else
00271             {
00272               message_out (DEBUG, "Element " + identifier + " closed, but there was no MainContainer to destroy (probably a malformed file)!");
00273             }
00274           }
00275           else
00276           {
00277             tmp_container_element = curr_container_element;
00278             curr_container_element = curr_container_element->getparent ();
00279             if (MainContainer != NULL)
00280             {
00281               tmp_container_element->add_to_main_tree();
00282               message_out (PARSER, "Element " + identifier + " closed, object added to MainContainer");
00283             }
00284             else
00285             {
00286               message_out (ERROR, "MainContainer is NULL trying to add element " + identifier);
00287             }
00288           }
00289         }
00290         else
00291         {
00292           message_out (ERROR, "Tried to close a " + identifier + " but a " + curr_container_element->type + " is currently open.");
00293         }
00294       }
00295     }
00296   }
00297 
00302   void data (const DataEvent & event)
00303   {
00304     string tmp;
00305     position = event.pos;
00306     AppendCharStringtostring (event.data, incoming_data);
00307     message_out(PARSER, "data event received from OpenSP, incoming_data is now: " + incoming_data);
00308   }
00309 
00314   void error (const ErrorEvent & event)
00315   {
00316     string message;
00317     string string_buf;
00318     OfxMsgType error_type = ERROR;
00319 
00320     position = event.pos;
00321     message = message + "OpenSP parser: ";
00322     switch (event.type)
00323     {
00324     case SGMLApplication::ErrorEvent::quantity:
00325       message = message + "quantity (Exceeding a quantity limit):";
00326       error_type = ERROR;
00327       break;
00328     case SGMLApplication::ErrorEvent::idref:
00329       message = message + "idref (An IDREF to a non-existent ID):";
00330       error_type = ERROR;
00331       break;
00332     case SGMLApplication::ErrorEvent::capacity:
00333       message = message + "capacity (Exceeding a capacity limit):";
00334       error_type = ERROR;
00335       break;
00336     case SGMLApplication::ErrorEvent::otherError:
00337       message = message + "otherError (misc parse error):";
00338       error_type = ERROR;
00339       break;
00340     case SGMLApplication::ErrorEvent::warning:
00341       message = message + "warning (Not actually an error.):";
00342       error_type = WARNING;
00343       break;
00344     case SGMLApplication::ErrorEvent::info:
00345       message =  message + "info (An informationnal message.  Not actually an error):";
00346       error_type = INFO;
00347       break;
00348     default:
00349       message = message + "OpenSP sent an unknown error to LibOFX (You probably have a newer version of OpenSP):";
00350     }
00351     message =   message + "\n" + CharStringtostring (event.message, string_buf);
00352     message_out (error_type, message);
00353   }
00354 
00359   void openEntityChange (const OpenEntityPtr & para_entity_ptr)
00360   {
00361     message_out(DEBUG, "openEntityChange()\n");
00362     entity_ptr = para_entity_ptr;
00363 
00364   };
00365 
00366 private:
00367 };
00368 
00372 int ofx_proc_sgml(LibofxContext * libofx_context, int argc, char * const* argv)
00373 {
00374   message_out(DEBUG, "Begin ofx_proc_sgml()");
00375   assert(argc >= 3);
00376   message_out(DEBUG, argv[0]);
00377   message_out(DEBUG, argv[1]);
00378   message_out(DEBUG, argv[2]);
00379 
00380   ParserEventGeneratorKit parserKit;
00381   parserKit.setOption (ParserEventGeneratorKit::showOpenEntities);
00382   EventGenerator *egp = parserKit.makeEventGenerator (argc, argv);
00383   egp->inhibitMessages (true);  /* Error output is handled by libofx not OpenSP */
00384   OFXApplication *app = new OFXApplication(libofx_context);
00385   unsigned nErrors = egp->run (*app); /* Begin parsing */
00386   delete egp;  //Note that this is where bug is triggered
00387   return nErrors > 0;
00388 }