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