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 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 }
OFCApplication
This object is driven by OpenSP as it parses the SGML from the ofx file(s)
Definition: ofc_sgml.cpp:45
OfxSecurityContainer
Represents a security, such as a stock or bond.
Definition: ofx_containers.hh:181
OfxGenericContainer::getparent
OfxGenericContainer * getparent()
Returns the parent container object (the one representing the containing OFX SGML element) ...
Definition: ofx_container_generic.cpp:63
OfxGenericContainer
A generic container for an OFX SGML element. Every container inherits from OfxGenericContainer.
Definition: ofx_containers.hh:33
INFO
Definition: messages.hh:32
OfxGenericContainer::add_attribute
virtual void add_attribute(const string identifier, const string value)
Add data to a container object.
Definition: ofx_container_generic.cpp:57
std
AppendCharStringtostring
string AppendCharStringtostring(const SGMLApplication::CharString source, string &dest)
Append an OpenSP CharString to an existing C++ STL string.
Definition: ofx_utilities.cpp:83
LibofxContext
Definition: context.hh:25
OFCApplication::endElement
void endElement(const EndElementEvent &event)
Callback: End of an OFX element.
Definition: ofc_sgml.cpp:211
WARNING
Definition: messages.hh:33
message_out
int message_out(OfxMsgType error_type, const string message)
Message output function.
Definition: messages.cpp:61
PARSER
Definition: messages.hh:35
OFCApplication::openEntityChange
void openEntityChange(const OpenEntityPtr &para_entity_ptr)
Callback: Receive internal OpenSP state.
Definition: ofc_sgml.cpp:340
DEBUG
Definition: messages.hh:25
OfxMsgType
OfxMsgType
Definition: messages.hh:23
ofc_proc_sgml
int ofc_proc_sgml(LibofxContext *libofx_context, int argc, char *const *argv)
Parses a DTD and OFX file(s)
Definition: ofc_sgml.cpp:353
CharStringtostring
string CharStringtostring(const SGMLApplication::CharString source, string &dest)
Convert OpenSP CharString to a C++ STL string.
Definition: ofx_utilities.cpp:70
OFCApplication::data
void data(const DataEvent &event)
Callback: Data from an OFX element.
Definition: ofc_sgml.cpp:283
ofx_utilities.hh
Various simple functions for type conversion & al.
ERROR
Definition: messages.hh:34
OFCApplication::startElement
void startElement(const StartElementEvent &event)
Callback: Start of an OFX element.
Definition: ofc_sgml.cpp:66
OfxGenericContainer::tag_identifier
string tag_identifier
Definition: ofx_containers.hh:37
OfxStatementContainer
Represents a statement for either a bank account or a credit card account.
Definition: ofx_containers.hh:134
OfxGenericContainer::add_to_main_tree
virtual int add_to_main_tree()
Add this container to the main tree.
Definition: ofx_container_generic.cpp:74
OfxInvestmentTransactionContainer
Represents a bank or credid card transaction.
Definition: ofx_containers.hh:232
OfxGenericContainer::type
string type
Definition: ofx_containers.hh:36
OfxPushUpContainer
A container to hold a OFX SGML element for which you want the parent to process it&#39;s data elements...
Definition: ofx_containers.hh:87
strip_whitespace
string strip_whitespace(const string para_string)
Sanitize a string coming from OpenSP.
Definition: ofx_utilities.cpp:253
ofx_containers.hh
LibOFX internal object code.
ofc_sgml.hh
OFX/SGML parsing functionnality.
OFCApplication::error
void error(const ErrorEvent &event)
Callback: SGML parse error.
Definition: ofc_sgml.cpp:295
messages.hh
Message IO functionality.
OfxBankTransactionContainer
Represents a bank or credid card transaction.
Definition: ofx_containers.hh:221
OfxAccountContainer
Represents a bank account or a credit card account.
Definition: ofx_containers.hh:157
OfxMainContainer
The root container. Created by the <OFX> OFX element or by the export functions.
Definition: ofx_containers.hh:247
OfxMainContainer::gen_event
int gen_event()
Generate libofx.h events.
Definition: ofx_container_main.cpp:154
OfxDummyContainer
A container to holds OFX SGML elements that LibOFX knows nothing about.
Definition: ofx_containers.hh:76
OfxBalanceContainer
Represents the <BALANCE> OFX SGML entity.
Definition: ofx_containers.hh:110
entity_ptr
SGMLApplication::OpenEntityPtr entity_ptr
Definition: messages.cpp:27
position
SGMLApplication::Position position
Definition: messages.cpp:28
OfxStatusContainer
Represents the <STATUS> OFX SGML entity.
Definition: ofx_containers.hh:96