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