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