LibOFX
lib/ofc_sgml.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  ofx_sgml.cpp
3  -------------------
4  copyright : (C) 2002 by Benoit Gr�goire
5  email : benoitg@coeus.ca
6 ***************************************************************************/
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <iostream>
26 #include <stdlib.h>
27 #include <string>
28 #include <cassert>
29 #include "ParserEventGeneratorKit.h"
30 #include "libofx.h"
31 #include "ofx_utilities.hh"
32 #include "messages.hh"
33 #include "ofx_containers.hh"
34 #include "ofc_sgml.hh"
35 
36 using namespace std;
37 
38 
39 extern SGMLApplication::OpenEntityPtr entity_ptr;
40 extern SGMLApplication::Position position;
41 extern OfxMainContainer * MainContainer;
42 
45 class OFCApplication : public SGMLApplication
46 {
47 private:
48  OfxGenericContainer *curr_container_element;
49  OfxGenericContainer *tmp_container_element;
50  bool is_data_element;
51  string incoming_data;
52  LibofxContext * libofx_context;
53 public:
54  OFCApplication (LibofxContext * p_libofx_context)
55  {
56  MainContainer = NULL;
57  curr_container_element = NULL;
58  is_data_element = false;
59  libofx_context = p_libofx_context;
60  }
61 
66  void startElement (const StartElementEvent & event)
67  {
68  string identifier;
69  CharStringtostring (event.gi, identifier);
70  message_out(PARSER, "startElement event received from OpenSP for element " + identifier);
71 
72  position = event.pos;
73 
74  switch (event.contentType)
75  {
76  case StartElementEvent::empty:
77  message_out(ERROR, "StartElementEvent::empty\n");
78  break;
79  case StartElementEvent::cdata:
80  message_out(ERROR, "StartElementEvent::cdata\n");
81  break;
82  case StartElementEvent::rcdata:
83  message_out(ERROR, "StartElementEvent::rcdata\n");
84  break;
85  case StartElementEvent::mixed:
86  message_out(PARSER, "StartElementEvent::mixed");
87  is_data_element = true;
88  break;
89  case StartElementEvent::element:
90  message_out(PARSER, "StartElementEvent::element");
91  is_data_element = false;
92  break;
93  default:
94  message_out(ERROR, "Unknown SGML content type?!?!?!? OpenSP interface changed?");
95  }
96 
97  if (is_data_element == false)
98  {
99  /*------- The following are OFC entities ---------------*/
100 
101  if (identifier == "OFC")
102  {
103  message_out (PARSER, "Element " + identifier + " found");
104  MainContainer = new OfxMainContainer (libofx_context, curr_container_element, identifier);
105  curr_container_element = MainContainer;
106  }
107  else if (identifier == "STATUS")
108  {
109  message_out (PARSER, "Element " + identifier + " found");
110  curr_container_element = new OfxStatusContainer (libofx_context, curr_container_element, identifier);
111  }
112  else if (identifier == "ACCTSTMT")
113  {
114  message_out (PARSER, "Element " + identifier + " found");
115  curr_container_element = new OfxStatementContainer (libofx_context, curr_container_element, identifier);
116  }
117  else if (identifier == "STMTRS")
118  {
119  message_out (PARSER, "Element " + identifier + " found");
120  //STMTRS ignored, we will process it's attributes directly inside the STATEMENT,
121  if (curr_container_element->type != "STATEMENT")
122  {
123  message_out(ERROR, "Element " + identifier + " found while not inside a STATEMENT container");
124  }
125  else
126  {
127  curr_container_element = new OfxPushUpContainer (libofx_context, curr_container_element, identifier);
128  }
129  }
130  else if (identifier == "GENTRN" ||
131  identifier == "STMTTRN")
132  {
133  message_out (PARSER, "Element " + identifier + " found");
134  curr_container_element = new OfxBankTransactionContainer (libofx_context, curr_container_element, identifier);
135  }
136  else if (identifier == "BUYDEBT" ||
137  identifier == "BUYMF" ||
138  identifier == "BUYOPT" ||
139  identifier == "BUYOTHER" ||
140  identifier == "BUYSTOCK" ||
141  identifier == "CLOSUREOPT" ||
142  identifier == "INCOME" ||
143  identifier == "INVEXPENSE" ||
144  identifier == "JRNLFUND" ||
145  identifier == "JRNLSEC" ||
146  identifier == "MARGININTEREST" ||
147  identifier == "REINVEST" ||
148  identifier == "RETOFCAP" ||
149  identifier == "SELLDEBT" ||
150  identifier == "SELLMF" ||
151  identifier == "SELLOPT" ||
152  identifier == "SELLOTHER" ||
153  identifier == "SELLSTOCK" ||
154  identifier == "SPLIT" ||
155  identifier == "TRANSFER" )
156  {
157  message_out (PARSER, "Element " + identifier + " found");
158  curr_container_element = new OfxInvestmentTransactionContainer (libofx_context, curr_container_element, identifier);
159  }
160  /*The following is a list of OFX elements whose attributes will be processed by the parent container*/
161  else if (identifier == "INVBUY" ||
162  identifier == "INVSELL" ||
163  identifier == "INVTRAN" ||
164  identifier == "SECID")
165  {
166  message_out (PARSER, "Element " + identifier + " found");
167  curr_container_element = new OfxPushUpContainer (libofx_context, curr_container_element, identifier);
168  }
169 
170  /* The different types of accounts */
171  else if (identifier == "ACCOUNT" ||
172  identifier == "ACCTFROM" )
173  {
174  message_out (PARSER, "Element " + identifier + " found");
175  curr_container_element = new OfxAccountContainer (libofx_context, curr_container_element, identifier);
176  }
177  else if (identifier == "SECINFO")
178  {
179  message_out (PARSER, "Element " + identifier + " found");
180  curr_container_element = new OfxSecurityContainer (libofx_context, curr_container_element, identifier);
181  }
182  /* The different types of balances */
183  else if (identifier == "LEDGERBAL" || identifier == "AVAILBAL")
184  {
185  message_out (PARSER, "Element " + identifier + " found");
186  curr_container_element = new OfxBalanceContainer (libofx_context, curr_container_element, identifier);
187  }
188  else
189  {
190  /* We dont know this OFX element, so we create a dummy container */
191  curr_container_element = new OfxDummyContainer(libofx_context, curr_container_element, identifier);
192  }
193  }
194  else
195  {
196  /* The element was a data element. OpenSP will call one or several data() callback with the data */
197  message_out (PARSER, "Data element " + identifier + " found");
198  /* 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 */
199  if (incoming_data != "")
200  {
201  message_out (ERROR, "startElement: incoming_data should be empty! You are probably using OpenSP <= 1.3.4. The following data was lost: " + incoming_data );
202  incoming_data.assign ("");
203  }
204  }
205  }
206 
211  void endElement (const EndElementEvent & event)
212  {
213  string identifier;
214  bool end_element_for_data_element;
215 
216  CharStringtostring (event.gi, identifier);
217  end_element_for_data_element = is_data_element;
218  message_out(PARSER, "endElement event received from OpenSP for element " + identifier);
219 
220  position = event.pos;
221  if (curr_container_element == NULL)
222  {
223  message_out (ERROR, "Tried to close a " + identifier + " without a open element (NULL pointer)");
224  incoming_data.assign ("");
225  }
226  else //curr_container_element != NULL
227  {
228  if (end_element_for_data_element == true)
229  {
230  incoming_data = strip_whitespace(incoming_data);
231 
232  curr_container_element->add_attribute (identifier, incoming_data);
233  message_out (PARSER, "endElement: Added data '" + incoming_data + "' from " + identifier + " to " + curr_container_element->type + " container_element");
234  incoming_data.assign ("");
235  is_data_element = false;
236  }
237  else
238  {
239  if (identifier == curr_container_element->tag_identifier)
240  {
241  if (incoming_data != "")
242  {
243  message_out(ERROR, "End tag for non data element " + identifier + ", incoming data should be empty but contains: " + incoming_data + " DATA HAS BEEN LOST SOMEWHERE!");
244  }
245 
246  if (identifier == "OFX")
247  {
248  /* The main container is a special case */
249  tmp_container_element = curr_container_element;
250  curr_container_element = curr_container_element->getparent ();
251  MainContainer->gen_event();
252  delete MainContainer;
253  MainContainer = NULL;
254  message_out (DEBUG, "Element " + identifier + " closed, MainContainer destroyed");
255  }
256  else
257  {
258  tmp_container_element = curr_container_element;
259  curr_container_element = curr_container_element->getparent ();
260  if (MainContainer != NULL)
261  {
262  tmp_container_element->add_to_main_tree();
263  message_out (PARSER, "Element " + identifier + " closed, object added to MainContainer");
264  }
265  else
266  {
267  message_out (ERROR, "MainContainer is NULL trying to add element " + identifier);
268  }
269  }
270  }
271  else
272  {
273  message_out (ERROR, "Tried to close a " + identifier + " but a " + curr_container_element->type + " is currently open.");
274  }
275  }
276  }
277  }
278 
283  void data (const DataEvent & event)
284  {
285  string tmp;
286  position = event.pos;
287  AppendCharStringtostring (event.data, incoming_data);
288  message_out(PARSER, "data event received from OpenSP, incoming_data is now: " + incoming_data);
289  }
290 
295  void error (const ErrorEvent & event)
296  {
297  string message;
298  string string_buf;
299  OfxMsgType error_type = ERROR;
300 
301  position = event.pos;
302  message = message + "OpenSP parser: ";
303  switch (event.type)
304  {
305  case SGMLApplication::ErrorEvent::quantity:
306  message = message + "quantity (Exceeding a quantity limit):";
307  error_type = ERROR;
308  break;
309  case SGMLApplication::ErrorEvent::idref:
310  message = message + "idref (An IDREF to a non-existent ID):";
311  error_type = ERROR;
312  break;
313  case SGMLApplication::ErrorEvent::capacity:
314  message = message + "capacity (Exceeding a capacity limit):";
315  error_type = ERROR;
316  break;
317  case SGMLApplication::ErrorEvent::otherError:
318  message = message + "otherError (misc parse error):";
319  error_type = ERROR;
320  break;
321  case SGMLApplication::ErrorEvent::warning:
322  message = message + "warning (Not actually an error.):";
323  error_type = WARNING;
324  break;
325  case SGMLApplication::ErrorEvent::info:
326  message = message + "info (An informationnal message. Not actually an error):";
327  error_type = INFO;
328  break;
329  default:
330  message = message + "OpenSP sent an unknown error to LibOFX (You probably have a newer version of OpenSP):";
331  }
332  message = message + "\n" + CharStringtostring (event.message, string_buf);
333  message_out (error_type, message);
334  }
335 
340  void openEntityChange (const OpenEntityPtr & para_entity_ptr)
341  {
342  message_out(DEBUG, "openEntityChange()\n");
343  entity_ptr = para_entity_ptr;
344 
345  };
346 
347 private:
348 };
349 
353 int ofc_proc_sgml(LibofxContext * libofx_context, int argc, char * const* argv)
354 {
355  message_out(DEBUG, "Begin ofx_proc_sgml()");
356  assert(argc >= 3);
357  message_out(DEBUG, argv[0]);
358  message_out(DEBUG, argv[1]);
359  message_out(DEBUG, argv[2]);
360 
361  ParserEventGeneratorKit parserKit;
362  parserKit.setOption (ParserEventGeneratorKit::showOpenEntities);
363  EventGenerator *egp = parserKit.makeEventGenerator (argc, argv);
364  egp->inhibitMessages (true); /* Error output is handled by libofx not OpenSP */
365  OFCApplication *app = new OFCApplication(libofx_context);
366  unsigned nErrors = egp->run (*app); /* Begin parsing */
367  delete egp;
368  return nErrors > 0;
369 }
This object is driven by OpenSP as it parses the SGML from the ofx file(s)
Represents a security, such as a stock or bond.
OfxGenericContainer * getparent()
Returns the parent container object (the one representing the containing OFX SGML element) ...
A generic container for an OFX SGML element. Every container inherits from OfxGenericContainer.
string CharStringtostring(const SGMLApplication::CharString source, string &dest)
Convert OpenSP CharString to a C++ STL string.
Various simple functions for type conversion & al.
virtual void add_attribute(const string identifier, const string value)
Add data to a container object.
string strip_whitespace(const string para_string)
Sanitize a string coming from OpenSP.
SGMLApplication::OpenEntityPtr entity_ptr
void endElement(const EndElementEvent &event)
Callback: End of an OFX element.
int message_out(OfxMsgType error_type, const string message)
Message output function.
OFX/SGML parsing functionnality.
void openEntityChange(const OpenEntityPtr &para_entity_ptr)
Callback: Receive internal OpenSP state.
string AppendCharStringtostring(const SGMLApplication::CharString source, string &dest)
Append an OpenSP CharString to an existing C++ STL string.
LibOFX internal object code.
void data(const DataEvent &event)
Callback: Data from an OFX element.
void startElement(const StartElementEvent &event)
Callback: Start of an OFX element.
Represents a statement for either a bank account or a credit card account.
Message IO functionality.
virtual int add_to_main_tree()
Add this container to the main tree.
Represents a bank or credid card transaction.
A container to hold a OFX SGML element for which you want the parent to process it&#39;s data elements...
SGMLApplication::Position position
void error(const ErrorEvent &event)
Callback: SGML parse error.
Represents a bank or credid card transaction.
Represents a bank account or a credit card account.
The root container. Created by the <OFX> OFX element or by the export functions.
int gen_event()
Generate libofx.h events.
A container to hold OFX SGML elements that LibOFX knows nothing about.
Represents the <BALANCE>, <INVBAL> or <INV401KBAL> OFX SGML entity.
int ofc_proc_sgml(LibofxContext *libofx_context, int argc, char *const *argv)
Parses a DTD and OFX file(s)
Represents the <STATUS> OFX SGML entity.