summaryrefslogtreecommitdiffstats
path: root/kmymoney2/converter/mymoneygncreader.h
blob: df0891366befe39a0789a5f7d5457d9563403922 (plain)
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
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