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