diff options
Diffstat (limited to 'kmymoney2/converter/mymoneygncreader.h')
-rw-r--r-- | kmymoney2/converter/mymoneygncreader.h | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/kmymoney2/converter/mymoneygncreader.h b/kmymoney2/converter/mymoneygncreader.h new file mode 100644 index 0000000..df08913 --- /dev/null +++ b/kmymoney2/converter/mymoneygncreader.h @@ -0,0 +1,904 @@ +/*************************************************************************** + mymoneygncreader - description + ------------------- + begin : Wed Mar 3 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@users.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. * + * * + ***************************************************************************/ +/* +The main class of this module, MyMoneyGncReader, contains only a readFile() +function, which controls the import of data from an XML file created by the +current GnuCash version (1.8.8). + +The XML is processed in class XmlReader, which is an implementation of the Qt +SAX2 reader class. + +Data in the input file is processed as a set of objects which fortunately, +though perhaps not surprisingly, have almost a one-for-one correspondence with +KMyMoney objects. These objects are bounded by start and end XML elements, and +may contain both nested objects (described as sub objects in the code), and data +items, also delimited by start and end elements. For example: +<gnc:account> * start of sub object within file + <act:name>Account Name</act:name> * data string with start and end elements + ... +</gnc:account> * end of sub objects + +A GnuCash file may consist of more than one 'book', or set of data. It is not +clear how we could currently implement this, so only the first book in a file is +processed. This should satisfy most user situations. + +GnuCash is somewhat inconsistent in its division of the major sections of the +file. For example, multiple price history entries are delimited by <gnc:pricedb> +elements, while each account starts with its own top-level element. In general, +the 'container' elements are ignored. + +XmlReader + +This is an implementation of the Qt QXmlDefaultHandler class, which provides +three main function calls in addition to start and end of document. The +startElement() and endElement() calls are self-explanatory, the characters() +function provides data strings. Thus in the above example, the sequence of calls +would be + startElement() for gnc:account + startElement() for act:name + characters() for 'Account Name' + endElement() for act:name + ... + endElement() for gnc:account + +Objects + +Since the processing requirements of XML for most elements are very similar, the +common code is implemented in a GncObject class, from which the others are +derived, with virtual function calls to cater for any differences. The +'grandfather' object, GncFile representing the file (or more correctly, 'book') +as a whole, is created in the startDocument() function call. + +The constructor function of each object is responsible for providing two lists +for the XmlReader to scan, a list of element names which represent sub objects +(called sub elements in the code), and a similar list of names representing data +elements. In addition, an array of variables (m_v) is provided and initialized, +to contain the actual data strings. + +Implementation + +Since objects may be nested, a stack is used, with the top element pointing to +the 'current object'. The startDocument() call creates the first, GncFile, +object at the top of the stack. + +As each startElement() call occurs, the two element lists created by the current +object are scanned. +If this element represents the start of a sub object, the current object's subEl() +function is called to create an instance of the appropriate type. This is then +pushed to the top of the stack, and the new object's initiate() function is +called. This is used to process any XML attributes attached to the element; +GnuCash makes little use of these. +If this represents the start of a data element, a pointer (m_dataPointer) is set +to point to an entry in the array (m_v) in which a subsequent characters() call +can store the actual data. + +When an endElement() call occurs, a check is made to see if it matches the +element name which started the current object. If so, the object's terminate() +function is called. If the object represents a similar KMM object, this will +normally result in a call to a conversion routine in the main +(MyMoneyGncReader) class to convert the data to native format and place it in +storage. The stack is then popped, and the parent (now current) object notified +by a call to its endSubEl() function. Again depending on the type of object, +this will either delete the instance, or save it in its own storage for later +processing. +For example, a GncSplit object makes little sense outside the context of its +transaction, so will be saved by the transaction. A GncTransaction object on the +other hand will be converted, along with its attendant splits, and then deleted +by its parent. + +Since at any one time an object will only be processing either a subobject or a +data element, a single object variable, m_state, is used to determine the actual +type. In effect, it acts as the current index into either the subElement or +dataElement list. As an object variable, it will be saved on the stack across +subobject processing. + +Exceptions and Problems + +Fatal exceptions are processed via the standard MyMoneyException method. +Due to differences in implementation between GnuCash and KMM, it is not always +possible to provide an absolutely correct conversion. When such a problem +situation is recognized, a message, along with any relevant variable data, is +passed to the main class, and used to produce a report when processing +terminates. The GncMessages and GncMessageArg classes implement this. + +Anonymizer + +When debugging problems, it is often useful to have a trace of what is happening +within the module. However, in view of the sensitive nature of personal finance +data, most users will be reluctant to provide this. Accordingly, an anonymize +(hide()) function is provided to handle data strings. These may either be passed +through asis (non-personal data), blanked out (non-critical but possibly personal +data), replaced with a generated version (required, but possibly personal), or +randomized (monetary amounts). The action for each data item is determined in +the object's constructor function along with the creation of the data element +list. +This module will later be used as the basis of a file anonymizer, which will +enable users to safely provide us with a copy of their GnuCash files, and will +allow us to test the structure, if not the data content, of the file. +*/ + +#ifndef MYMONEYSTORAGEGNC_H +#define MYMONEYSTORAGEGNC_H + +// Some STL headers in GCC4.3 contain operator new. Memory checker mangles these +#ifdef _CHECK_MEMORY + #undef new +#endif +// system includes +#include <stdlib.h> + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdatastream.h> +class QIODevice; +#include <qobject.h> +#include <qvaluelist.h> +#include <qptrlist.h> +#include <qptrstack.h> +#include <qxml.h> +#include <qdatetime.h> +#include <qtextcodec.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#ifdef _CHECK_MEMORY + #include <kmymoney/mymoneyutils.h> +#endif + +#ifndef _GNCFILEANON +#include "../mymoney/storage/imymoneyserialize.h" // not used any more, but call interface requires it +#include "../mymoney/storage/imymoneystorageformat.h" +#endif // _GNCFILEANON + +// not sure what these are for, but leave them in +#define VERSION_0_60_XML 0x10000010 // Version 0.5 file version info +#define VERSION_0_61_XML 0x10000011 // use 8 bytes for MyMoneyMoney objects +#define GNUCASH_ID_KEY "GNUCASH_ID" + +typedef QMap<QString, QString> map_accountIds; +typedef map_accountIds::iterator map_accountIds_iter; +typedef map_accountIds::const_iterator map_accountIds_citer; + +typedef QMap<QString, QStringList> map_elementVersions; + +class MyMoneyGncReader; + +/** GncObject is the base class for the various objects in the gnucash file + Beyond the first level XML objects, elements will be of one of three types: + 1. Sub object elements, which require creation of another object to process + 2. Data object elements, which are only followed by data to be stored in a variable (m_v array) + 3. Ignored objects, data not needed and not included herein +*/ +class GncObject { +public: + GncObject(); + ; // to save delete loop when finished + virtual ~GncObject() {} // make sure to have impl of all virtual rtns to avoid vtable errors? +protected: + friend class XmlReader; + friend class MyMoneyGncReader; + + // check for sub object element; if it is, create the object + GncObject *isSubElement (const QString &elName, const QXmlAttributes& elAttrs); + // check for data element; if so, set data pointer + bool isDataElement (const QString &elName, const QXmlAttributes& elAttrs); + // process start element for 'this'; normally for attribute checking; other initialization done in constructor + virtual void initiate (const QString&, const QXmlAttributes&) { return ;}; + // a sub object has completed; process the data it gathered + virtual void endSubEl(GncObject *) {m_dataPtr = 0; return ;}; + // store data for data element + void storeData (const QString& pData) // NB - data MAY come in chunks, and may need to be anonymized + {if (m_dataPtr != 0) + m_dataPtr->append (hide (pData, m_anonClass)); return ;} + // following is provided only for a future file anonymizer + QString getData () const { return ((m_dataPtr != 0) ? *m_dataPtr : "");}; + void resetDataPtr() {m_dataPtr = 0;}; + // process end element for 'this'; usually to convert to KMM format + virtual void terminate() { return ;}; + void setVersion (const QString& v) {m_version = v; return; }; + QString version() const {return (m_version);}; + + // some gnucash elements have version attribute; check it + void checkVersion (const QString&, const QXmlAttributes&, const map_elementVersions&); + // get name of element processed by 'this' + QString getElName () const { return (m_elementName);}; + // pass 'main' pointer to object + void setPm (MyMoneyGncReader *pM) {pMain = pM;}; + // debug only + void debugDump(); + + // called by isSubElement to create appropriate sub object + virtual GncObject *startSubEl() { return (0);}; + // called by isDataElement to set variable pointer + virtual void dataEl(const QXmlAttributes&) {m_dataPtr = m_v.at(m_state); m_anonClass = m_anonClassList[m_state];}; + // return gnucash data string variable pointer + virtual QString var (int i) const; + // anonymize data + virtual QString hide (QString, unsigned int); + + MyMoneyGncReader *pMain; // pointer to 'main' class + // used at start of each transaction so same money hide factor is applied to all splits + void adjustHideFactor(); + + QString m_elementName; // save 'this' element's name + QString m_version; // and it's gnucash version + const QString *m_subElementList; // list of sub object element names for 'this' + unsigned int m_subElementListCount; // count of above + const QString *m_dataElementList; // ditto for data elements + unsigned int m_dataElementListCount; + QString *m_dataPtr; // pointer to m_v variable for current data item + mutable QPtrList<QString> m_v; // storage for variable pointers + + unsigned int m_state; // effectively, the index to subElementList or dataElementList, whichever is currently in use + + const unsigned int *m_anonClassList; + enum anonActions {ASIS, SUPPRESS, NXTACC, NXTEQU, NXTPAY, NXTSCHD, MAYBEQ, MONEY1, MONEY2}; // anonymize actions - see hide() + unsigned int m_anonClass; // class of current data item for anonymizer + static double m_moneyHideFactor; // a per-transaction factor +}; + +// ***************************************************************************** +// This is the 'grandfather' object representing the gnucash file as a whole +class GncFile : public GncObject { +public: + GncFile (); + ~GncFile(); +private: + enum iSubEls {BOOK, COUNT, CMDTY, PRICE, ACCT, TX, TEMPLATES, SCHEDULES, END_FILE_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + + bool m_processingTemplates; // gnc uses same transaction element for ordinary and template tx's; this will distinguish + bool m_bookFound; // to detect multi-book files +}; +// The following are 'utility' objects, which occur within several other object types +// **************************************************************************** +// commodity specification. consists of +// cmdty:space - either ISO4217 if this cmdty is a currency, or, usually, the name of a stock exchange +// cmdty:id - ISO4217 currency symbol, or 'ticker symbol' +class GncCmdtySpec : public GncObject { +public: + GncCmdtySpec (); + ~GncCmdtySpec (); +protected: + friend class MyMoneyGncReader; + friend class GncTransaction; + bool isCurrency() const { return (*m_v.at(CMDTYSPC) == QString("ISO4217"));}; + QString id() const { return (*m_v.at(CMDTYID));}; + QString space() const { return (*m_v.at(CMDTYSPC));}; +private: + // data elements + enum CmdtySpecDataEls {CMDTYSPC, CMDTYID, END_CmdtySpec_DELS}; + virtual QString hide (QString, unsigned int); +}; +// ********************************************************************* +// date; maybe one of two types, ts:date which is date/time, gdate which is date only +// we do not preserve time data (at present) +class GncDate : public GncObject { +public: + GncDate (); + ~GncDate(); +protected: + friend class MyMoneyGncReader; + friend class GncPrice; + friend class GncTransaction; + friend class GncSplit; + friend class GncSchedule; + friend class GncRecurrence; + const QDate date() const { return (QDate::fromString(m_v.at(TSDATE)->section(' ', 0, 0), Qt::ISODate));}; +private: + // data elements + enum DateDataEls {TSDATE, GDATE, END_Date_DELS}; + virtual void dataEl(const QXmlAttributes&) {m_dataPtr = m_v.at(TSDATE); m_anonClass = GncObject::ASIS;} + ; // treat both date types the same +}; +// ************* GncKvp******************************************** +// Key/value pairs, which are introduced by the 'slot' element +// Consist of slot:key (the 'name' of the kvp), and slot:value (the data value) +// the slot value also contains a slot type (string, integer, etc) implemented as an XML attribute +// kvp's may be nested +class GncKvp : public GncObject { +public: + GncKvp (); + ~GncKvp(); +protected: + friend class MyMoneyGncReader; + + QString key() const { return (var(KEY));}; + QString value() const { return (var(VALUE));}; + QString type() const { return (m_kvpType);}; + unsigned int kvpCount() const { return (m_kvpList.count());}; + const GncKvp *getKvp(unsigned int i) const { return (static_cast<GncKvp *>(m_kvpList.at(i)));}; +private: + // subsidiary objects/elements + enum KvpSubEls {KVP, END_Kvp_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum KvpDataEls {KEY, VALUE, END_Kvp_DELS }; + virtual void dataEl (const QXmlAttributes&); + mutable QPtrList<GncObject> m_kvpList; + QString m_kvpType; // type is an XML attribute +}; +// ************* GncLot******************************************** +// KMM doesn't have support for lots as yet +class GncLot : public GncObject { + public: + GncLot (); + ~GncLot(); + protected: + friend class MyMoneyGncReader; + private: +}; + +/** Following are the main objects within the gnucash file, which correspond largely one-for-one + with similar objects in the kmymoney structure, apart from schedules which gnc splits between + template (transaction data) and schedule (date data) +*/ +//******************************************************************** +class GncCountData : public GncObject { +public: + GncCountData (); + ~GncCountData (); +private: + virtual void initiate (const QString&, const QXmlAttributes&); + virtual void terminate(); + QString m_countType; // type of element being counted +}; +//******************************************************************** +class GncCommodity : public GncObject { +public: + GncCommodity (); + ~GncCommodity(); +protected: + friend class MyMoneyGncReader; + // access data values + bool isCurrency() const { return (var(SPACE) == QString("ISO4217"));}; + QString space() const { return (var(SPACE));}; + QString id() const { return (var(ID));}; + QString name() const { return (var(NAME));}; + QString fraction() const { return (var(FRACTION));}; +private: + virtual void terminate(); + // data elements + enum {SPACE, ID, NAME, FRACTION, END_Commodity_DELS}; +}; +// ************* GncPrice******************************************** +class GncPrice : public GncObject { +public: + GncPrice (); + ~GncPrice(); +protected: + friend class MyMoneyGncReader; + // access data values + const GncCmdtySpec *commodity() const { return (m_vpCommodity);}; + const GncCmdtySpec *currency() const { return (m_vpCurrency);}; + QString value() const { return (var(VALUE));}; + QDate priceDate () const { return (m_vpPriceDate->date());}; +private: + virtual void terminate(); + // sub object elements + enum PriceSubEls {CMDTY, CURR, PRICEDATE, END_Price_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum PriceDataEls {VALUE, END_Price_DELS }; + GncCmdtySpec *m_vpCommodity, *m_vpCurrency; + GncDate *m_vpPriceDate; +}; +// ************* GncAccount******************************************** +class GncAccount : public GncObject { +public: + GncAccount (); + ~GncAccount(); +protected: + friend class MyMoneyGncReader; + // access data values + GncCmdtySpec *commodity() const { return (m_vpCommodity);}; + QString id () const { return (var(ID));}; + QString name () const { return (var(NAME));}; + QString desc () const { return (var(DESC));}; + QString type () const { return (var(TYPE));}; + QString parent () const { return (var(PARENT));}; +private: + // subsidiary objects/elements + enum AccountSubEls {CMDTY, KVP, LOTS, END_Account_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + virtual void terminate(); + // data elements + enum AccountDataEls {ID, NAME, DESC, TYPE, PARENT, END_Account_DELS }; + GncCmdtySpec *m_vpCommodity; + QPtrList<GncObject> m_kvpList; +}; +// ************* GncSplit******************************************** +class GncSplit : public GncObject { +public: + GncSplit (); + ~GncSplit(); +protected: + friend class MyMoneyGncReader; + // access data values + QString id() const { return (var(ID));}; + QString memo() const { return (var(MEMO));}; + QString recon() const { return (var(RECON));}; + QString value() const { return (var(VALUE));}; + QString qty() const { return (var(QTY));}; + QString acct() const { return (var(ACCT));}; +const QDate reconDate() const {QDate x = QDate(); return (m_vpDateReconciled == NULL ? x : m_vpDateReconciled->date());}; +private: + // subsidiary objects/elements + enum TransactionSubEls {RECDATE, END_Split_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum SplitDataEls {ID, MEMO, RECON, VALUE, QTY, ACCT, END_Split_DELS }; + GncDate *m_vpDateReconciled; +}; +// ************* GncTransaction******************************************** +class GncTransaction : public GncObject { +public: + GncTransaction (bool processingTemplates); + ~GncTransaction(); +protected: + friend class MyMoneyGncReader; + // access data values + QString id() const { return (var(ID));}; + QString no() const { return (var(NO));}; + QString desc() const { return (var(DESC));}; + QString currency() const { return (m_vpCurrency == NULL ? QString () : m_vpCurrency->id());}; + QDate dateEntered() const { return (m_vpDateEntered->date());}; + QDate datePosted() const { return (m_vpDatePosted->date());}; + bool isTemplate() const { return (m_template);}; + unsigned int splitCount() const { return (m_splitList.count());}; + unsigned int kvpCount() const { return (m_kvpList.count());}; + const GncObject *getSplit (unsigned int i) const { return (m_splitList.at(i));}; + const GncKvp *getKvp(unsigned int i) const { return (static_cast<GncKvp *>(m_kvpList.at(i)));}; +private: + // subsidiary objects/elements + enum TransactionSubEls {CURRCY, POSTED, ENTERED, SPLIT, KVP, END_Transaction_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + virtual void terminate(); + // data elements + enum TransactionDataEls {ID, NO, DESC, END_Transaction_DELS }; + GncCmdtySpec *m_vpCurrency; + GncDate *m_vpDateEntered, *m_vpDatePosted; + mutable QPtrList<GncObject> m_splitList; + bool m_template; // true if this is a template for scheduled transaction + mutable QPtrList<GncObject> m_kvpList; +}; + +// ************* GncTemplateSplit******************************************** +class GncTemplateSplit : public GncObject { +public: + GncTemplateSplit (); + ~GncTemplateSplit(); +protected: + friend class MyMoneyGncReader; + // access data values + QString id() const { return (var(ID));}; + QString memo() const { return (var(MEMO));}; + QString recon() const { return (var(RECON));}; + QString value() const { return (var(VALUE));}; + QString qty() const { return (var(QTY));}; + QString acct() const { return (var(ACCT));}; + unsigned int kvpCount() const { return (m_kvpList.count());}; + const GncKvp *getKvp(unsigned int i) const { return (static_cast<GncKvp *>(m_kvpList.at(i)));}; +private: + // subsidiary objects/elements + enum TemplateSplitSubEls {KVP, END_TemplateSplit_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum TemplateSplitDataEls {ID, MEMO, RECON, VALUE, QTY, ACCT, END_TemplateSplit_DELS }; + mutable QPtrList<GncObject> m_kvpList; +}; +// ************* GncSchedule******************************************** +class GncFreqSpec; +class GncRecurrence; +class GncSchedDef; +class GncSchedule : public GncObject { +public: + GncSchedule (); + ~GncSchedule(); +protected: + friend class MyMoneyGncReader; + // access data values + QString name() const { return (var(NAME));}; + QString enabled() const {return var(ENABLED);}; + QString autoCreate() const { return (var(AUTOC));}; + QString autoCrNotify() const { return (var(AUTOCN));}; + QString autoCrDays() const { return (var(AUTOCD));}; + QString advCrDays() const { return (var(ADVCD));}; + QString advCrRemindDays() const { return (var(ADVRD));}; + QString instanceCount() const { return (var(INSTC));}; + QString numOccurs() const { return (var(NUMOCC));}; + QString remOccurs() const { return (var(REMOCC));}; + QString templId() const { return (var(TEMPLID));}; + QDate startDate () const + {QDate x = QDate(); return (m_vpStartDate == NULL ? x : m_vpStartDate->date());}; + QDate lastDate () const + {QDate x = QDate(); return (m_vpLastDate == NULL ? x : m_vpLastDate->date());}; + QDate endDate() const + {QDate x = QDate(); return (m_vpEndDate == NULL ? x : m_vpEndDate->date());}; + const GncFreqSpec *getFreqSpec() const { return (m_vpFreqSpec);}; + const GncSchedDef *getSchedDef() const { return (m_vpSchedDef);}; +private: + // subsidiary objects/elements + enum ScheduleSubEls {STARTDATE, LASTDATE, ENDDATE, FREQ, RECURRENCE, DEFINST, END_Schedule_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + virtual void terminate(); + // data elements + enum ScheduleDataEls {NAME, ENABLED, AUTOC, AUTOCN, AUTOCD, ADVCD, ADVRD, INSTC, + NUMOCC, REMOCC, TEMPLID, END_Schedule_DELS }; + GncDate *m_vpStartDate, *m_vpLastDate, *m_vpEndDate; + GncFreqSpec *m_vpFreqSpec; + mutable QPtrList<GncRecurrence> m_vpRecurrence; // gnc handles multiple occurrences + GncSchedDef *m_vpSchedDef; +}; +// ************* GncFreqSpec******************************************** +class GncFreqSpec : public GncObject { +public: + GncFreqSpec (); + ~GncFreqSpec(); +protected: + friend class MyMoneyGncReader; + // access data values (only interval type used at present) + QString intervalType() const { return (var(INTVT));}; +private: + // subsidiary objects/elements + enum FreqSpecSubEls {COMPO, END_FreqSpec_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum FreqSpecDataEls {INTVT, MONTHLY, DAILY, WEEKLY, INTVI, INTVO, INTVD, END_FreqSpec_DELS}; + virtual void terminate(); + mutable QPtrList<GncObject> m_fsList; +}; + +// ************* GncRecurrence******************************************** +// this object replaces GncFreqSpec from Gnucash 2.2 onwards +class GncRecurrence : public GncObject { +public: + GncRecurrence (); + ~GncRecurrence(); +protected: + friend class MyMoneyGncReader; + // access data values + QDate startDate () const + {QDate x = QDate(); return (m_vpStartDate == NULL ? x : m_vpStartDate->date());}; + QString mult() const {return (var(MULT));}; + QString periodType() const {return (var(PERIODTYPE));}; + QString getFrequency() const; +private: + // subsidiary objects/elements + enum RecurrenceSubEls {STARTDATE, END_Recurrence_SELS }; + virtual GncObject *startSubEl(); + virtual void endSubEl(GncObject *); + // data elements + enum RecurrenceDataEls {MULT, PERIODTYPE, END_Recurrence_DELS}; + virtual void terminate(); + GncDate *m_vpStartDate; +}; + +// ************* GncSchedDef******************************************** +// This is a sub-object of GncSchedule, (sx:deferredInstance) function currently unknown +class GncSchedDef : public GncObject { + public: + GncSchedDef (); + ~GncSchedDef(); + protected: + friend class MyMoneyGncReader; + private: + // subsidiary objects/elements +}; + +// **************************************************************************************** +/** + XML Reader + The XML reader is an implementation of the Qt SAX2 XML parser. It determines the type + of object represented by the XMl, and calls the appropriate object functions +*/ +// ***************************************************************************************** +class XmlReader : public QXmlDefaultHandler { +protected: + friend class MyMoneyGncReader; + XmlReader (MyMoneyGncReader *pM) : pMain(pM) {} // keep pointer to 'main' + void processFile (QIODevice*); // main entry point of reader + // define xml content handler functions + bool startDocument (); + bool startElement (const QString&, const QString&, const QString&, const QXmlAttributes&); + bool endElement (const QString&, const QString&, const QString&); + bool characters (const QString &); + bool endDocument(); +private: + QXmlInputSource *m_source; + QXmlSimpleReader *m_reader; + QPtrStack<GncObject> m_os; // stack of sub objects + GncObject *m_co; // current object, for ease of coding (=== m_os.top) + MyMoneyGncReader *pMain; // the 'main' pointer, to pass on to objects + bool m_headerFound; // check for gnc-v2 header +#ifdef _GNCFILEANON + int lastType; // 0 = start element, 1 = data, 2 = end element + int indentCount; +#endif // _GNCFILEANON +}; + +/** + * private classes to define messages to be held in list for final report + */ +class GncMessageArgs { +protected: + friend class MyMoneyGncReader; + QString source; // 'type of message + unsigned int code; // to identify actual message + QValueList<QString> args; // variable arguments +}; + +class GncMessages { +protected: + friend class MyMoneyGncReader; + static QString text (const QString, const unsigned int); // returns text of identified message + static unsigned int argCount (const QString, const unsigned int); // returns no. of args required +private: + typedef struct { + const QString source; + const unsigned int code; + QString text; + } + messText; + static messText texts []; +}; + +/** + MyMoneyGncReader - Main class for this module + Controls overall operation of the importer + */ + +#ifndef _GNCFILEANON +class MyMoneyGncReader : public IMyMoneyStorageFormat { +#else +class MyMoneyGncReader { +#endif // _GNCFILEANON +public: + MyMoneyGncReader(); + virtual ~MyMoneyGncReader(); + /** + * Import a GnuCash XML file + * + * @param pDevice : pointer to GnuCash file + * @param storage : pointer to MyMoneySerialize storage + * + * @return void + * + */ +#ifndef _GNCFILEANON + void readFile (QIODevice* pDevice, IMyMoneySerialize* storage); // main entry point, IODevice is gnucash file + void writeFile (QIODevice*, IMyMoneySerialize*) { return ;}; // dummy entry needed by kmymoneywiew. we will not be writing +#else + void readFile (QString, QString); +#endif // _GNCFILEANON + QTextCodec *m_decoder; +protected: + friend class GncObject; // pity we can't just say GncObject. And compiler doesn't like multiple friends on one line... + friend class GncFile; // there must be a better way... + friend class GncDate; + friend class GncCmdtySpec; + friend class GncKvp; + friend class GncLot; + friend class GncCountData; + friend class GncCommodity; + friend class GncPrice; + friend class GncAccount; + friend class GncTransaction; + friend class GncSplit; + friend class GncTemplateTransaction; + friend class GncTemplateSplit; + friend class GncSchedule; + friend class GncFreqSpec; + friend class GncRecurrence; + friend class XmlReader; +#ifndef _GNCFILEANON + /** functions to convert gnc objects to our equivalent */ + void convertCommodity (const GncCommodity *); + void convertPrice (const GncPrice *); + void convertAccount (const GncAccount *); + void convertTransaction (const GncTransaction *); + void convertSplit (const GncSplit *); + void saveTemplateTransaction (GncTransaction *t) {m_templateList.append (t);}; + void convertSchedule (const GncSchedule *); + void convertFreqSpec (const GncFreqSpec *); + void convertRecurrence (const GncRecurrence *); +#else + /** functions to convert gnc objects to our equivalent */ + void convertCommodity (const GncCommodity *) {return;}; + void convertPrice (const GncPrice *) {return;}; + void convertAccount (const GncAccount *) {return;}; + void convertTransaction (const GncTransaction *) {return;}; + void convertSplit (const GncSplit *) {return;}; + void saveTemplateTransaction (GncTransaction *t) {return;}; + void convertSchedule (const GncSchedule *) {return;}; + void convertFreqSpec (const GncFreqSpec *) {return;}; +#endif // _GNCFILEANON +/** to post messages for final report */ + void postMessage (const QString&, const unsigned int, const char *); + void postMessage (const QString&, const unsigned int, const char *, const char *); + void postMessage (const QString&, const unsigned int, const char *, const char *, const char *); + void postMessage (const QString&, const unsigned int, const QStringList&); + void setProgressCallback (void(*callback)(int, int, const QString&)); + void signalProgress (int current, int total, const QString& = ""); + /** user options */ + /** + Scheduled Transactions + Due to differences in implementation, it is not always possible to import scheduled + transactions correctly. Though best efforts are made, it may be that some + imported transactions cause problems within kmymoney. + An attempt is made within the importer to identify potential problem transactions, + and setting this option will cause them to be dropped from the file. + A report of which were dropped, and why, will be produced. + m_dropSuspectSchedules - drop suspect scheduled transactions + */ + bool m_dropSuspectSchedules; + /** + Investments + In kmymoney, all accounts representing investments (stocks, shares, bonds, etc.) must + have an associated investment account (e.g. a broker account). The stock account holds + the share balance, the investment account a money balance. + Gnucash does not do this, so we cannot automate this function. If you have investments, + you must select one of the following options. + 0 - create a separate investment account for each stock with the same name as the stock + 1 - create a single investment account to hold all stocks - you will be asked for a name + 2 - create multiple investment accounts - you will be asked for a name for each stock + N.B. :- option 2 doesn't really work quite as desired at present + */ + unsigned int m_investmentOption; + /** Online quotes + The user has the option to use the Finance::Quote system, as used by GnuCash, to + retrieve online share price quotes + */ + bool m_useFinanceQuote; + /** Tx Notes handling + Under some usage conditions, non-split GnuCash transactions may contain residual, usually incorrect, memo + data which is not normally visible to the user. When imported into KMyMoney however, due to display + differences, this data can become visible. Often, these transactions will have a Notes field describing + the real purpose of the transaction. If this option is selected, these notes, if present, will be used to + override the extraneous memo data." */ + bool m_useTxNotes; + // set gnucash counts (not always accurate!) + void setGncCommodityCount(int i) { m_gncCommodityCount = i;}; + void setGncAccountCount (int i) { m_gncAccountCount = i;}; + void setGncTransactionCount (int i) { m_gncTransactionCount = i;}; + void setGncScheduleCount (int i) { m_gncScheduleCount = i;}; + void setSmallBusinessFound (bool b) { m_smallBusinessFound = b;}; + void setBudgetsFound (bool b) { m_budgetsFound = b;}; + void setLotsFound (bool b) { m_lotsFound = b;}; + /* Debug Options + If you don't know what these are, best leave them alone. + gncdebug - produce general debug messages + xmldebug - produce a trace of the gnucash file XML + bAnonymize - hide personal data (account names, payees, etc., randomize money amounts) + */ + bool gncdebug; // general debug messages + bool xmldebug; // xml trace + bool bAnonymize; // anonymize input + static double m_fileHideFactor; // an overall anonymization factor to be applied to all items + bool developerDebug; +private: + void setOptions (); // to set user options from dialog + void setFileHideFactor (); + // the following handles the gnucash indicator for a bad value (-1/0) which causes us probs + QString convBadValue (QString gncValue) const {return (gncValue == "-1/0" ? "0/1" : gncValue); }; +#ifndef _GNCFILEANON + MyMoneyTransaction convertTemplateTransaction (const QString&, const GncTransaction *); + void convertTemplateSplit (const QString&, const GncTemplateSplit *); +#endif // _GNCFILEANON + // wind up when all done + void terminate(); + QString buildReportSection (const QString&); + bool writeReportToFile (const QValueList<QString>&); + // main storage +#ifndef _GNCFILEANON + IMyMoneyStorage *m_storage; +#else + QTextStream oStream; +#endif // _GNCFILEANON + XmlReader *m_xr; + /** to hold the callback pointer for the progress bar */ + void (*m_progressCallback)(int, int, const QString&); + // a map of which versions of the various elements (objects) we can import + map_elementVersions m_versionList; + // counters holding count data from the Gnc 'count-data' section + int m_gncCommodityCount; + int m_gncAccountCount; + int m_gncTransactionCount; + int m_gncScheduleCount; + + // flags indicating detection of features not (yet?) supported + bool m_smallBusinessFound; + bool m_budgetsFound; + bool m_lotsFound; + + /** counters for reporting */ + int m_commodityCount; + int m_priceCount; + int m_accountCount; + int m_transactionCount; + int m_templateCount; + int m_scheduleCount; +#ifndef _GNCFILEANON + // counters for error reporting + int m_ccCount, m_orCount, m_scCount; + // currency counter + QMap<QString, unsigned int> m_currencyCount; + /** + * Map gnucash vs. Kmm ids for accounts, equities, schedules, price sources + */ + QMap<QString, QString> m_mapIds; + QString m_rootId; // save the root id for terminate() + QMap<QString, QString> m_mapEquities; + QMap<QString, QString> m_mapSchedules; + QMap<QString, QString> m_mapSources; + /** + * A list of stock accounts (gnc ids) which will be held till the end + so we can implement the user's investment option + */ + QValueList<QString> m_stockList; + /** + * Temporary storage areas for transaction processing + */ + QString m_txCommodity; // save commodity for current transaction + QString m_txPayeeId; // gnc has payee at tx level, we need it at split level + QDate m_txDatePosted; // ditto for post date + QString m_txChequeNo; // ditto for cheque number + /** In kmm, the order of splits is critical to some operations. These + * areas will hold the splits until we've read them all */ + QValueList<MyMoneySplit> m_splitList, m_liabilitySplitList, m_otherSplitList; + bool m_potentialTransfer; // to determine whether this might be a transfer + /** Schedules are processed through 3 different functions, any of which may set this flag */ + bool m_suspectSchedule; + /** + * A holding area for template txs while we're waiting for the schedules + */ + QPtrList<GncTransaction> m_templateList; + /** Hold a list of suspect schedule ids for later processing? */ + QValueList<QString> m_suspectList; + /** + * To hold message data till final report + */ + QPtrList<GncMessageArgs> m_messageList; + GncMessages *m_messageTexts; + /** + * Internal utility functions + */ + QString createPayee (const QString&); // create a payee and return it's id + QString createOrphanAccount (const QString&); // create unknown account and return the id + QDate incrDate (QDate lastDate, unsigned char interval, unsigned int intervalCount); // for date calculations + MyMoneyAccount checkConsistency (MyMoneyAccount& parent, MyMoneyAccount& child); // gnucash is sometimes TOO flexible + void checkInvestmentOption (QString stockId); // implement user investment option + void getPriceSource (MyMoneySecurity stock, QString gncSource); +#endif // _GNCFILEANON +}; + +#endif // MYMONEYSTORAGEGNC_H |