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