1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.moneywise.quicken.file;
18
19 import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
20 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
21 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCategoryBase;
22 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
23 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
24 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit.MoneyWiseDepositList;
25 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
26 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
27 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
28 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice;
29 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice.MoneyWiseSecurityPriceList;
30 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
31 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
32 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransTag;
33 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
34 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction.MoneyWiseTransactionList;
35 import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
36 import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQIFPreference.MoneyWiseQIFPreferenceKey;
37 import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQIFPreference.MoneyWiseQIFPreferences;
38 import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQIFType;
39
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45
46
47
48
49 public class MoneyWiseQIFFile {
50
51
52
53 protected static final int HASH_BASE = 37;
54
55
56
57
58 protected static final String HOLDING_SUFFIX = "Holding";
59
60
61
62
63 private final MoneyWiseQIFType theFileType;
64
65
66
67
68 private OceanusDate theStartDate;
69
70
71
72
73 private OceanusDate theLastDate;
74
75
76
77
78 private final Map<String, MoneyWiseQIFAccountEvents> theAccountMap;
79
80
81
82
83 private final List<MoneyWiseQIFAccountEvents> theAccounts;
84
85
86
87
88 private final Map<String, MoneyWiseQIFPayee> thePayeeMap;
89
90
91
92
93 private final List<MoneyWiseQIFPayee> thePayees;
94
95
96
97
98 private final Map<String, MoneyWiseQIFSecurityPrices> theSecurityMap;
99
100
101
102
103 private final List<MoneyWiseQIFSecurityPrices> theSecurities;
104
105
106
107
108 private final Map<String, MoneyWiseQIFSecurity> theSymbolMap;
109
110
111
112
113 private final Map<String, MoneyWiseQIFParentCategory> theParentMap;
114
115
116
117
118 private final List<MoneyWiseQIFParentCategory> theParentCategories;
119
120
121
122
123 private final Map<String, MoneyWiseQIFEventCategory> theCategories;
124
125
126
127
128 private final Map<String, MoneyWiseQIFClass> theClassMap;
129
130
131
132
133 private final List<MoneyWiseQIFClass> theClasses;
134
135
136
137
138
139
140 public MoneyWiseQIFFile(final MoneyWiseQIFType pType) {
141
142 theFileType = pType;
143
144
145 theAccountMap = new HashMap<>();
146 thePayeeMap = new HashMap<>();
147 theSecurityMap = new HashMap<>();
148 theSymbolMap = new HashMap<>();
149 theParentMap = new HashMap<>();
150 theCategories = new HashMap<>();
151 theClassMap = new HashMap<>();
152
153
154 theAccounts = new ArrayList<>();
155 thePayees = new ArrayList<>();
156 theSecurities = new ArrayList<>();
157 theParentCategories = new ArrayList<>();
158 theClasses = new ArrayList<>();
159 }
160
161
162
163
164
165
166 public MoneyWiseQIFType getFileType() {
167 return theFileType;
168 }
169
170
171
172
173
174
175 protected boolean hasClasses() {
176 return !theClasses.isEmpty();
177 }
178
179
180
181
182
183
184 protected int numClasses() {
185 return theClasses.size();
186 }
187
188
189
190
191
192
193 protected Iterator<MoneyWiseQIFClass> classIterator() {
194 return theClasses.iterator();
195 }
196
197
198
199
200
201
202 protected int numCategories() {
203 return theCategories.size();
204 }
205
206
207
208
209
210
211 protected Iterator<MoneyWiseQIFParentCategory> categoryIterator() {
212 return theParentCategories.iterator();
213 }
214
215
216
217
218
219
220 protected int numAccounts() {
221 return theAccounts.size();
222 }
223
224
225
226
227
228
229 protected Iterator<MoneyWiseQIFAccountEvents> accountIterator() {
230 return theAccounts.iterator();
231 }
232
233
234
235
236
237
238 protected boolean hasSecurities() {
239 return !theSecurities.isEmpty();
240 }
241
242
243
244
245
246
247 protected int numSecurities() {
248 return theSecurities.size();
249 }
250
251
252
253
254
255
256 protected Iterator<MoneyWiseQIFSecurityPrices> securityIterator() {
257 return theSecurities.iterator();
258 }
259
260
261
262
263 protected void sortLists() {
264
265 theClasses.sort(null);
266
267
268 thePayees.sort(null);
269
270
271 theParentCategories.sort(null);
272 final Iterator<MoneyWiseQIFParentCategory> myCatIterator = categoryIterator();
273 while (myCatIterator.hasNext()) {
274 final MoneyWiseQIFParentCategory myParent = myCatIterator.next();
275
276
277 myParent.sortChildren();
278 }
279
280
281 theSecurities.sort(null);
282 final Iterator<MoneyWiseQIFSecurityPrices> mySecIterator = securityIterator();
283 while (mySecIterator.hasNext()) {
284 final MoneyWiseQIFSecurityPrices mySecurity = mySecIterator.next();
285
286
287 mySecurity.sortPrices();
288 }
289
290
291 theAccounts.sort(null);
292 final Iterator<MoneyWiseQIFAccountEvents> myAccIterator = accountIterator();
293 while (myAccIterator.hasNext()) {
294 final MoneyWiseQIFAccountEvents myAccount = myAccIterator.next();
295
296
297 myAccount.sortEvents();
298 }
299 }
300
301
302
303
304
305
306
307
308
309 public static MoneyWiseQIFFile buildQIFFile(final MoneyWiseDataSet pData,
310 final MoneyWiseAnalysis pAnalysis,
311 final MoneyWiseQIFPreferences pPreferences) {
312
313 final MoneyWiseQIFType myType = pPreferences.getEnumValue(MoneyWiseQIFPreferenceKey.QIFTYPE, MoneyWiseQIFType.class);
314 final OceanusDate myLastDate = pPreferences.getDateValue(MoneyWiseQIFPreferenceKey.LASTEVENT);
315
316
317 final MoneyWiseQIFFile myFile = new MoneyWiseQIFFile(myType);
318
319
320 myFile.buildData(pData, pAnalysis, myLastDate);
321 myFile.sortLists();
322
323
324 return myFile;
325 }
326
327
328
329
330
331
332
333 public MoneyWiseQIFClass registerClass(final MoneyWiseTransTag pClass) {
334
335 final String myName = pClass.getName();
336 return theClassMap.computeIfAbsent(myName, n -> {
337 final MoneyWiseQIFClass myClass = new MoneyWiseQIFClass(this, pClass);
338 theClasses.add(myClass);
339 return myClass;
340 });
341 }
342
343
344
345
346
347
348 public void registerClass(final MoneyWiseQIFClass pClass) {
349
350 final String myName = pClass.getName();
351 theClassMap.computeIfAbsent(myName, n -> {
352 theClasses.add(pClass);
353 return pClass;
354 });
355 }
356
357
358
359
360
361
362
363 public MoneyWiseQIFEventCategory registerCategory(final MoneyWiseTransCategory pCategory) {
364
365 final String myName = pCategory.getName();
366 return theCategories.computeIfAbsent(myName, n -> {
367 final MoneyWiseQIFEventCategory myCat = new MoneyWiseQIFEventCategory(this, pCategory);
368 registerCategoryToParent(pCategory.getParentCategory(), myCat);
369 return myCat;
370 });
371 }
372
373
374
375
376
377
378
379 private void registerCategoryToParent(final MoneyWiseTransCategory pParent,
380 final MoneyWiseQIFEventCategory pCategory) {
381
382 final String myName = pParent.getName();
383 final MoneyWiseQIFParentCategory myParent = theParentMap.computeIfAbsent(myName, n -> {
384 final MoneyWiseQIFParentCategory myParCat = new MoneyWiseQIFParentCategory(this, pParent);
385 theParentCategories.add(myParCat);
386 return myParCat;
387 });
388
389
390 myParent.registerChild(pCategory);
391 }
392
393
394
395
396
397
398 public void registerCategory(final MoneyWiseQIFEventCategory pCategory) {
399
400 final String myName = pCategory.getName();
401 final MoneyWiseQIFEventCategory myCat = theCategories.get(myName);
402 if (myCat == null) {
403
404 final int myPos = myName.indexOf(MoneyWiseCategoryBase.STR_SEP);
405
406
407 if (myPos < 0) {
408
409 final MoneyWiseQIFParentCategory myParent = new MoneyWiseQIFParentCategory(pCategory);
410 theParentMap.put(myName, myParent);
411 theParentCategories.add(myParent);
412
413
414 } else {
415
416 theCategories.put(myName, pCategory);
417
418
419 final String myParentName = myName.substring(0, myPos);
420
421
422 final MoneyWiseQIFParentCategory myParent = theParentMap.get(myParentName);
423
424
425 myParent.registerChild(pCategory);
426 }
427 }
428 }
429
430
431
432
433
434
435
436 public MoneyWiseQIFAccountEvents registerAccount(final MoneyWiseTransAsset pAccount) {
437
438 final String myName = pAccount.getName();
439 return theAccountMap.computeIfAbsent(myName, n -> {
440 final MoneyWiseQIFAccountEvents myAccount = new MoneyWiseQIFAccountEvents(this, pAccount);
441 theAccounts.add(myAccount);
442 return myAccount;
443 });
444 }
445
446
447
448
449
450
451
452 public MoneyWiseQIFAccountEvents registerHoldingAccount(final MoneyWisePortfolio pPortfolio) {
453
454 final String myName = pPortfolio.getName() + HOLDING_SUFFIX;
455 return theAccountMap.computeIfAbsent(myName, n -> {
456 final MoneyWiseQIFAccountEvents myAccount = new MoneyWiseQIFAccountEvents(this, myName);
457 theAccounts.add(myAccount);
458 return myAccount;
459 });
460 }
461
462
463
464
465
466
467
468 public MoneyWiseQIFAccountEvents registerAccount(final MoneyWiseQIFAccount pAccount) {
469
470 final String myName = pAccount.getName();
471 return theAccountMap.computeIfAbsent(myName, n -> {
472 final MoneyWiseQIFAccountEvents myAccount = new MoneyWiseQIFAccountEvents(pAccount);
473 theAccounts.add(myAccount);
474 return myAccount;
475 });
476 }
477
478
479
480
481
482
483
484 public MoneyWiseQIFPayee registerPayee(final MoneyWisePayee pPayee) {
485
486 final String myName = pPayee.getName();
487 return thePayeeMap.computeIfAbsent(myName, n -> {
488 final MoneyWiseQIFPayee myPayee = new MoneyWiseQIFPayee(pPayee);
489 thePayees.add(myPayee);
490 return myPayee;
491 });
492 }
493
494
495
496
497
498
499
500 public MoneyWiseQIFPayee registerPayee(final String pPayee) {
501
502 return thePayeeMap.computeIfAbsent(pPayee, n -> {
503 final MoneyWiseQIFPayee myPayee = new MoneyWiseQIFPayee(pPayee);
504 thePayees.add(myPayee);
505 return myPayee;
506 });
507 }
508
509
510
511
512
513
514
515 public MoneyWiseQIFSecurity registerSecurity(final MoneyWiseSecurity pSecurity) {
516
517 final String myName = pSecurity.getName();
518 final MoneyWiseQIFSecurityPrices mySecurity = theSecurityMap.computeIfAbsent(myName, n -> {
519 final MoneyWiseQIFSecurityPrices mySec = new MoneyWiseQIFSecurityPrices(this, pSecurity);
520 theSymbolMap.put(pSecurity.getSymbol(), mySec.getSecurity());
521 theSecurities.add(mySec);
522 return mySec;
523 });
524
525
526 return mySecurity.getSecurity();
527 }
528
529
530
531
532
533
534 public void registerSecurity(final MoneyWiseQIFSecurity pSecurity) {
535
536 final String myName = pSecurity.getName();
537 theSecurityMap.computeIfAbsent(myName, n -> {
538 final MoneyWiseQIFSecurityPrices mySecurity = new MoneyWiseQIFSecurityPrices(this, pSecurity);
539 theSymbolMap.put(pSecurity.getSymbol(), mySecurity.getSecurity());
540 theSecurities.add(mySecurity);
541 return mySecurity;
542 });
543 }
544
545
546
547
548
549
550 public void registerPrice(final MoneyWiseSecurityPrice pPrice) {
551
552 final MoneyWiseSecurity mySecurity = pPrice.getSecurity();
553 final MoneyWiseQIFSecurityPrices mySecurityList = theSecurityMap.get(mySecurity.getName());
554 if (mySecurityList != null) {
555
556 mySecurityList.addPrice(pPrice);
557 }
558 }
559
560
561
562
563
564
565 public void registerPrice(final MoneyWiseQIFPrice pPrice) {
566
567 final MoneyWiseQIFSecurity mySecurity = pPrice.getSecurity();
568 final MoneyWiseQIFSecurityPrices mySecurityList = theSecurityMap.get(mySecurity.getName());
569 if (mySecurityList != null) {
570
571 final Iterator<MoneyWiseQIFPrice> myIterator = pPrice.priceIterator();
572 while (myIterator.hasNext()) {
573 final MoneyWiseQIFPrice myPrice = myIterator.next();
574
575
576 mySecurityList.addPrice(myPrice);
577 }
578 }
579 }
580
581
582
583
584
585
586
587 protected MoneyWiseQIFEventCategory getCategory(final String pName) {
588
589 return theCategories.get(pName);
590 }
591
592
593
594
595
596
597
598 protected MoneyWiseQIFAccount getAccount(final String pName) {
599
600 final MoneyWiseQIFAccountEvents myAccount = getAccountEvents(pName);
601 return (myAccount == null)
602 ? null
603 : myAccount.getAccount();
604 }
605
606
607
608
609
610
611
612 protected MoneyWiseQIFAccountEvents getAccountEvents(final String pName) {
613
614 return theAccountMap.get(pName);
615 }
616
617
618
619
620
621
622
623 protected MoneyWiseQIFSecurity getSecurity(final String pName) {
624
625 final MoneyWiseQIFSecurityPrices myList = getSecurityPrices(pName);
626 return myList == null
627 ? null
628 : myList.getSecurity();
629 }
630
631
632
633
634
635
636
637 protected MoneyWiseQIFSecurity getSecurityBySymbol(final String pSymbol) {
638
639 return theSymbolMap.get(pSymbol);
640 }
641
642
643
644
645
646
647
648 protected MoneyWiseQIFSecurityPrices getSecurityPrices(final String pName) {
649
650 return theSecurityMap.get(pName);
651 }
652
653
654
655
656
657
658
659 protected MoneyWiseQIFClass getClass(final String pName) {
660
661 return theClassMap.get(pName);
662 }
663
664
665
666
667
668
669
670
671 public void buildData(final MoneyWiseDataSet pData,
672 final MoneyWiseAnalysis pAnalysis,
673 final OceanusDate pLastDate) {
674
675 final MoneyWiseQIFBuilder myBuilder = new MoneyWiseQIFBuilder(this, pData, pAnalysis);
676
677
678 theStartDate = pData.getDateRange().getStart();
679 theLastDate = pLastDate;
680
681
682 buildOpeningBalances(myBuilder, pData.getDeposits());
683
684
685 final MoneyWiseTransactionList myEvents = pData.getTransactions();
686 final Iterator<MoneyWiseTransaction> myIterator = myEvents.iterator();
687 while (myIterator.hasNext()) {
688 final MoneyWiseTransaction myEvent = myIterator.next();
689
690
691 final OceanusDate myDate = myEvent.getDate();
692 if (myDate.compareTo(pLastDate) > 0) {
693 break;
694 }
695
696
697 myBuilder.processEvent(myEvent);
698 }
699
700
701 buildPrices(pData.getSecurityPrices());
702 }
703
704
705
706
707
708
709
710 private void buildOpeningBalances(final MoneyWiseQIFBuilder pBuilder,
711 final MoneyWiseDepositList pDepositList) {
712
713 final Iterator<MoneyWiseDeposit> myIterator = pDepositList.iterator();
714 while (myIterator.hasNext()) {
715 final MoneyWiseDeposit myDeposit = myIterator.next();
716
717
718 final OceanusMoney myBalance = myDeposit.getOpeningBalance();
719 if (myBalance == null) {
720 continue;
721 }
722
723
724 pBuilder.processBalance(myDeposit, theStartDate, myBalance);
725 }
726 }
727
728
729
730
731
732
733 private void buildPrices(final MoneyWiseSecurityPriceList pPriceList) {
734
735 final Iterator<MoneyWiseSecurityPrice> myIterator = pPriceList.iterator();
736 while (myIterator.hasNext()) {
737 final MoneyWiseSecurityPrice myPrice = myIterator.next();
738
739
740 final OceanusDate myDate = myPrice.getDate();
741 if (myDate.compareTo(theLastDate) > 0) {
742 break;
743 }
744
745
746 registerPrice(myPrice);
747 }
748 }
749
750 @Override
751 public boolean equals(final Object pThat) {
752
753 if (this == pThat) {
754 return true;
755 }
756 if (pThat == null) {
757 return false;
758 }
759
760
761 if (!(pThat instanceof MoneyWiseQIFFile)) {
762 return false;
763 }
764
765
766 final MoneyWiseQIFFile myThat = (MoneyWiseQIFFile) pThat;
767
768
769 if (!theFileType.equals(myThat.theFileType)) {
770 return false;
771 }
772
773
774 if (!theClasses.equals(myThat.theClasses)) {
775 return false;
776 }
777
778
779 if (!theParentCategories.equals(myThat.theParentCategories)) {
780 return false;
781 }
782
783
784 if (!theSecurities.equals(myThat.theSecurities)) {
785 return false;
786 }
787
788
789 if (!thePayees.equals(myThat.thePayees)) {
790 return false;
791 }
792
793
794 return theAccounts.equals(myThat.theAccounts);
795 }
796
797 @Override
798 public int hashCode() {
799 int myResult = MoneyWiseQIFFile.HASH_BASE * theFileType.hashCode();
800 myResult += theClasses.hashCode();
801 myResult *= MoneyWiseQIFFile.HASH_BASE;
802 myResult += theParentCategories.hashCode();
803 myResult *= MoneyWiseQIFFile.HASH_BASE;
804 myResult += theSecurities.hashCode();
805 myResult *= MoneyWiseQIFFile.HASH_BASE;
806 myResult += thePayees.hashCode();
807 myResult *= MoneyWiseQIFFile.HASH_BASE;
808 myResult += theAccounts.hashCode();
809 return myResult;
810 }
811 }