1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.moneywise.data.basic;
18
19 import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
20 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataMap;
21 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataObjectFormat;
22 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
23 import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
24 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio.MoneyWisePortfolioList;
25 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityList;
26 import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
27 import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityClass;
28 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
29 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
30 import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
31
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Currency;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39
40
41
42
43 public final class MoneyWiseSecurityHolding
44 implements MetisFieldItem, MoneyWiseTransAsset, Comparable<Object> {
45
46
47
48 public static final String SECURITYHOLDING_SEP = ":";
49
50
51
52
53 public static final String SECURITYHOLDING_NEW = MoneyWiseBasicResource.SECURITYHOLDING_NEW.getValue();
54
55
56
57
58 private static final MetisFieldSet<MoneyWiseSecurityHolding> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityHolding.class);
59
60
61
62
63 static {
64 FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAITEM_ID, MoneyWiseSecurityHolding::getExternalId);
65 FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAITEM_FIELD_NAME, MoneyWiseSecurityHolding::getName);
66 FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.PORTFOLIO, MoneyWiseSecurityHolding::getPortfolio);
67 FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.SECURITY, MoneyWiseSecurityHolding::getSecurity);
68 }
69
70
71
72
73 private final Long theId;
74
75
76
77
78 private MoneyWisePortfolio thePortfolio;
79
80
81
82
83 private MoneyWiseSecurity theSecurity;
84
85
86
87
88
89
90
91 private MoneyWiseSecurityHolding(final MoneyWisePortfolio pPortfolio,
92 final MoneyWiseSecurity pSecurity) {
93
94 thePortfolio = pPortfolio;
95 theSecurity = pSecurity;
96
97
98 theId = MoneyWiseAssetType.createExternalId(MoneyWiseAssetType.SECURITYHOLDING, thePortfolio.getIndexedId(), theSecurity.getIndexedId());
99 }
100
101 @Override
102 public MetisFieldSet<MoneyWiseSecurityHolding> getDataFieldSet() {
103 return FIELD_DEFS;
104 }
105
106 @Override
107 public String formatObject(final OceanusDataFormatter pFormatter) {
108 return toString();
109 }
110
111 @Override
112 public String toString() {
113 return getName();
114 }
115
116 @Override
117 public Boolean isClosed() {
118
119 final MoneyWisePortfolio myPortfolio = getPortfolio();
120 final MoneyWiseSecurity mySecurity = getSecurity();
121
122
123 return myPortfolio.isClosed()
124 || mySecurity.isClosed();
125 }
126
127
128
129
130
131
132 public boolean isDeleted() {
133
134 final MoneyWisePortfolio myPortfolio = getPortfolio();
135 final MoneyWiseSecurity mySecurity = getSecurity();
136
137
138 return myPortfolio.isDeleted()
139 || mySecurity.isDeleted();
140 }
141
142 @Override
143 public Long getExternalId() {
144 return theId;
145 }
146
147 @Override
148 public String getName() {
149 return generateName();
150 }
151
152 @Override
153 public MoneyWisePayee getParent() {
154 return theSecurity.getParent();
155 }
156
157 @Override
158 public boolean isTaxFree() {
159 return thePortfolio.isTaxFree();
160 }
161
162 @Override
163 public boolean isGross() {
164 return thePortfolio.isGross();
165 }
166
167 @Override
168 public boolean isShares() {
169 return theSecurity.isShares();
170 }
171
172 @Override
173 public boolean isCapital() {
174 return theSecurity.isCapital();
175 }
176
177 @Override
178 public boolean isAutoExpense() {
179 return false;
180 }
181
182 @Override
183 public boolean isHidden() {
184 return false;
185 }
186
187
188
189
190
191
192 public MoneyWisePortfolio getPortfolio() {
193 return thePortfolio;
194 }
195
196
197
198
199
200
201 public MoneyWiseSecurity getSecurity() {
202 return theSecurity;
203 }
204
205 @Override
206 public MoneyWiseAssetType getAssetType() {
207 return MoneyWiseAssetType.SECURITYHOLDING;
208 }
209
210 @Override
211 public MoneyWiseCurrency getAssetCurrency() {
212 return theSecurity.getAssetCurrency();
213 }
214
215 @Override
216 public Currency getCurrency() {
217 return getAssetCurrency().getCurrency();
218 }
219
220 @Override
221 public boolean isForeign() {
222 final MoneyWiseCurrency myDefault = thePortfolio.getDataSet().getReportingCurrency();
223 return !myDefault.equals(getAssetCurrency());
224 }
225
226
227
228
229
230
231 private String generateName() {
232
233 final StringBuilder myBuilder = new StringBuilder();
234 myBuilder.append(thePortfolio.getName());
235 myBuilder.append(SECURITYHOLDING_SEP);
236 myBuilder.append(theSecurity.getName());
237 return myBuilder.toString();
238 }
239
240
241
242
243
244
245 private MoneyWiseSecurityClass getSecurityTypeClass() {
246
247 return theSecurity.getCategoryClass();
248 }
249
250
251
252
253
254
255
256 public boolean isSecurityClass(final MoneyWiseSecurityClass pClass) {
257
258 return getSecurityTypeClass() == pClass;
259 }
260
261 @Override
262 public void touchItem(final PrometheusDataItem pSource) {
263
264 thePortfolio.touchItem(pSource);
265 theSecurity.touchItem(pSource);
266 }
267
268 @Override
269 public boolean equals(final Object pThat) {
270
271 if (this == pThat) {
272 return true;
273 }
274 if (pThat == null) {
275 return false;
276 }
277 if (!(pThat instanceof MoneyWiseSecurityHolding)) {
278 return false;
279 }
280
281
282 final MoneyWiseSecurityHolding myThat = (MoneyWiseSecurityHolding) pThat;
283 if (!getPortfolio().equals(myThat.getPortfolio())) {
284 return false;
285 }
286
287
288 return getSecurity().equals(myThat.getSecurity());
289 }
290
291 @Override
292 public int hashCode() {
293 final int myHash = MetisFieldSet.HASH_PRIME * getPortfolio().hashCode();
294 return myHash + getSecurity().hashCode();
295 }
296
297 @Override
298 public int compareTo(final Object pThat) {
299
300 if (this.equals(pThat)) {
301 return 0;
302 }
303 if (pThat == null) {
304 return -1;
305 }
306
307
308 if (!(pThat instanceof MoneyWiseSecurityHolding)) {
309 return 1;
310 }
311
312
313 final MoneyWiseSecurityHolding myThat = (MoneyWiseSecurityHolding) pThat;
314
315
316 int iDiff = getPortfolio().compareTo(myThat.getPortfolio());
317 if (iDiff == 0) {
318
319 iDiff = getSecurity().compareTo(myThat.getSecurity());
320 }
321
322
323 return iDiff;
324 }
325
326
327
328
329
330
331 protected boolean validCurrencies() {
332 return thePortfolio.getCurrency().equals(theSecurity.getCurrency());
333 }
334
335
336
337
338 public static class MoneyWiseSecurityHoldingMap
339 implements MetisDataObjectFormat, MetisDataMap<Integer, MoneyWisePortfolioHoldingsMap> {
340
341
342
343 private final MoneyWisePortfolioList thePortfolios;
344
345
346
347
348 private final MoneyWiseSecurityList theSecurities;
349
350
351
352
353 private final Map<Integer, MoneyWisePortfolioHoldingsMap> theMap;
354
355
356
357
358
359
360 public MoneyWiseSecurityHoldingMap(final MoneyWiseDataSet pData) {
361
362 thePortfolios = pData.getPortfolios();
363 theSecurities = pData.getSecurities();
364 theMap = new HashMap<>();
365 }
366
367
368
369
370
371
372 public MoneyWiseSecurityHoldingMap(final PrometheusEditSet pEditSet) {
373
374 thePortfolios = pEditSet.getDataList(MoneyWiseBasicDataType.PORTFOLIO, MoneyWisePortfolioList.class);
375 theSecurities = pEditSet.getDataList(MoneyWiseBasicDataType.SECURITY, MoneyWiseSecurityList.class);
376 theMap = new HashMap<>();
377 }
378
379 @Override
380 public Map<Integer, MoneyWisePortfolioHoldingsMap> getUnderlyingMap() {
381 return theMap;
382 }
383
384 @Override
385 public String formatObject(final OceanusDataFormatter pFormatter) {
386 return MoneyWiseSecurityHoldingMap.class.getSimpleName();
387 }
388
389
390
391
392 public void clear() {
393 theMap.clear();
394 }
395
396
397
398
399
400
401
402 public MoneyWiseSecurityHolding findHoldingById(final Long pId) {
403
404 final Integer myPortId = getPortfolioId(pId);
405 final Integer mySecId = getSecurityId(pId);
406
407
408 final MoneyWisePortfolioHoldingsMap myMap = getMapForPortfolio(myPortId);
409 return myMap == null
410 ? null
411 : myMap.getHoldingForSecurity(mySecId);
412 }
413
414
415
416
417
418
419
420 public MoneyWiseSecurityHolding findHoldingByName(final String pName) {
421
422 final String myPortName = getPortfolioName(pName);
423 final String mySecName = getSecurityName(pName);
424
425
426 final MoneyWisePortfolioHoldingsMap myMap = getMapForPortfolio(myPortName);
427 return myMap == null
428 ? null
429 : myMap.getHoldingForSecurity(mySecName);
430 }
431
432
433
434
435
436
437
438 private static String getPortfolioName(final String pName) {
439 final int iIndex = pName.indexOf(SECURITYHOLDING_SEP);
440 return iIndex == -1
441 ? pName
442 : pName.substring(0, iIndex);
443 }
444
445
446
447
448
449
450
451 private static String getSecurityName(final String pName) {
452 final int iIndex = pName.indexOf(SECURITYHOLDING_SEP);
453 return iIndex == -1
454 ? ""
455 : pName.substring(iIndex + 1);
456 }
457
458
459
460
461
462
463
464 private static Integer getPortfolioId(final Long pId) {
465 return MoneyWiseAssetType.getMajorId(pId);
466 }
467
468
469
470
471
472
473
474 private static Integer getSecurityId(final Long pId) {
475 return MoneyWiseAssetType.getBaseId(pId);
476 }
477
478
479
480
481
482
483
484
485 public MoneyWiseSecurityHolding declareHolding(final MoneyWisePortfolio pPortfolio,
486 final MoneyWiseSecurity pSecurity) {
487
488 final MoneyWisePortfolioHoldingsMap myPortMap = getMapForPortfolio(pPortfolio.getIndexedId());
489 if (myPortMap == null) {
490 throw new IllegalStateException("Invalid Portfolio");
491 }
492 final Map<Integer, MoneyWiseSecurityHolding> myMap = myPortMap.getUnderlyingMap();
493
494
495 final Integer myId = pSecurity.getIndexedId();
496 return myMap.computeIfAbsent(myId, i -> new MoneyWiseSecurityHolding(pPortfolio, pSecurity));
497 }
498
499
500
501
502
503
504
505 private MoneyWisePortfolioHoldingsMap getMapForPortfolio(final Integer pId) {
506
507 MoneyWisePortfolioHoldingsMap myMap = theMap.get(pId);
508
509
510 if (myMap == null) {
511
512 final MoneyWisePortfolio myPortfolio = thePortfolios.findItemById(pId);
513
514
515 if (myPortfolio == null
516 || myPortfolio.isDeleted()) {
517 return null;
518 }
519
520
521 myMap = new MoneyWisePortfolioHoldingsMap(myPortfolio, theSecurities);
522 theMap.put(pId, myMap);
523 }
524
525
526 return myMap;
527 }
528
529
530
531
532
533
534
535 private MoneyWisePortfolioHoldingsMap getMapForPortfolio(final String pName) {
536
537 final MoneyWisePortfolio myPortfolio = thePortfolios.findItemByName(pName);
538
539
540 if (myPortfolio == null) {
541 return null;
542 }
543
544
545 final Integer myId = myPortfolio.getIndexedId();
546 return theMap.computeIfAbsent(myId, i -> new MoneyWisePortfolioHoldingsMap(myPortfolio, theSecurities));
547 }
548
549
550
551
552
553
554 public void deRegister(final MoneyWisePortfolio pPortfolio) {
555
556 final Integer myId = pPortfolio.getIndexedId();
557 theMap.remove(myId);
558 }
559
560
561
562
563
564
565 public void deRegister(final MoneyWiseSecurity pSecurity) {
566
567 final Integer myId = pSecurity.getIndexedId();
568
569
570 final Iterator<MoneyWisePortfolioHoldingsMap> myIterator = theMap.values().iterator();
571 while (myIterator.hasNext()) {
572 final MoneyWisePortfolioHoldingsMap myMap = myIterator.next();
573
574
575 myMap.getUnderlyingMap().remove(myId);
576 }
577 }
578
579
580
581
582
583
584
585 public Iterator<MoneyWiseSecurityHolding> existingIterator(final MoneyWisePortfolio pPortfolio) {
586
587 final MoneyWisePortfolioHoldingsMap myMap = theMap.get(pPortfolio.getIndexedId());
588
589
590 return myMap == null
591 ? null
592 : myMap.existingIterator();
593 }
594
595
596
597
598
599
600
601 public Iterator<MoneyWiseSecurityHolding> newIterator(final MoneyWisePortfolio pPortfolio) {
602 return newIterator(pPortfolio, null);
603 }
604
605
606
607
608
609
610
611
612 public Iterator<MoneyWiseSecurityHolding> newIterator(final MoneyWisePortfolio pPortfolio,
613 final MoneyWiseSecurityClass pClass) {
614
615 final MoneyWisePortfolioHoldingsMap myMap = theMap.get(pPortfolio.getIndexedId());
616
617
618 return myMap == null
619 ? fullIterator(pPortfolio, pClass)
620 : myMap.newIterator(pClass);
621 }
622
623
624
625
626
627
628
629
630 private Iterator<MoneyWiseSecurityHolding> fullIterator(final MoneyWisePortfolio pPortfolio,
631 final MoneyWiseSecurityClass pClass) {
632
633 if (pPortfolio.isClosed() || pPortfolio.isDeleted()) {
634
635 return null;
636 }
637
638
639 List<MoneyWiseSecurityHolding> myList = new ArrayList<>();
640
641
642 final Iterator<MoneyWiseSecurity> myIterator = theSecurities.iterator();
643 while (myIterator.hasNext()) {
644 final MoneyWiseSecurity mySecurity = myIterator.next();
645
646
647 boolean bIgnore = mySecurity.isClosed() || mySecurity.isDeleted();
648 bIgnore |= pClass != null && !pClass.equals(mySecurity.getCategoryClass());
649 if (bIgnore) {
650 continue;
651 }
652
653
654 final MoneyWiseSecurityHolding myHolding = new MoneyWiseSecurityHolding(pPortfolio, mySecurity);
655 myList.add(myHolding);
656 }
657
658
659 if (myList.isEmpty()) {
660 myList = null;
661 } else {
662 Collections.sort(myList);
663 }
664
665
666 return myList == null
667 ? null
668 : myList.iterator();
669 }
670 }
671
672
673
674
675 public static final class MoneyWisePortfolioHoldingsMap
676 implements MetisDataObjectFormat, MetisDataMap<Integer, MoneyWiseSecurityHolding> {
677
678
679
680 private final MoneyWisePortfolio thePortfolio;
681
682
683
684
685 private final MoneyWiseSecurityList theSecurities;
686
687
688
689
690 private final Map<Integer, MoneyWiseSecurityHolding> theMap;
691
692
693
694
695
696
697
698 private MoneyWisePortfolioHoldingsMap(final MoneyWisePortfolio pPortfolio,
699 final MoneyWiseSecurityList pSecurities) {
700 thePortfolio = pPortfolio;
701 theSecurities = pSecurities;
702 theMap = new HashMap<>();
703 }
704
705 @Override
706 public Map<Integer, MoneyWiseSecurityHolding> getUnderlyingMap() {
707 return theMap;
708 }
709
710 @Override
711 public String formatObject(final OceanusDataFormatter pFormatter) {
712 return thePortfolio.formatObject(pFormatter);
713 }
714
715 @Override
716 public String toString() {
717 return thePortfolio.toString();
718 }
719
720
721
722
723
724
725
726 private MoneyWiseSecurityHolding getHoldingForSecurity(final Integer pId) {
727
728 MoneyWiseSecurityHolding myHolding = theMap.get(pId);
729
730
731 if (myHolding == null) {
732
733 final MoneyWiseSecurity mySecurity = theSecurities.findItemById(pId);
734
735
736 if (mySecurity == null || mySecurity.isDeleted()) {
737 return null;
738 }
739
740
741 myHolding = new MoneyWiseSecurityHolding(thePortfolio, mySecurity);
742 theMap.put(pId, myHolding);
743 }
744
745
746 return myHolding;
747 }
748
749
750
751
752
753
754
755 private MoneyWiseSecurityHolding getHoldingForSecurity(final String pName) {
756
757 final MoneyWiseSecurity mySecurity = theSecurities.findItemByName(pName);
758
759
760 if (mySecurity == null) {
761 return null;
762 }
763
764
765 final Integer myId = mySecurity.getIndexedId();
766 return theMap.computeIfAbsent(myId, i -> new MoneyWiseSecurityHolding(thePortfolio, mySecurity));
767 }
768
769
770
771
772
773
774 private Iterator<MoneyWiseSecurityHolding> existingIterator() {
775
776 if (thePortfolio.isClosed() || thePortfolio.isDeleted()) {
777
778 return null;
779 }
780
781
782 List<MoneyWiseSecurityHolding> myList = new ArrayList<>();
783
784
785 final Iterator<MoneyWiseSecurityHolding> myIterator = theMap.values().iterator();
786 while (myIterator.hasNext()) {
787 final MoneyWiseSecurityHolding myHolding = myIterator.next();
788 final MoneyWiseSecurity mySecurity = myHolding.getSecurity();
789
790
791 if (!mySecurity.isClosed() && !mySecurity.isDeleted()) {
792
793 myList.add(myHolding);
794 }
795 }
796
797
798 if (myList.isEmpty()) {
799 myList = null;
800 } else {
801 Collections.sort(myList);
802 }
803
804
805 return myList == null
806 ? null
807 : myList.iterator();
808 }
809
810
811
812
813
814
815
816 private Iterator<MoneyWiseSecurityHolding> newIterator(final MoneyWiseSecurityClass pClass) {
817
818 if (thePortfolio.isClosed() || thePortfolio.isDeleted()) {
819
820 return null;
821 }
822
823
824 List<MoneyWiseSecurityHolding> myList = new ArrayList<>();
825
826
827 final Iterator<MoneyWiseSecurity> myIterator = theSecurities.iterator();
828 while (myIterator.hasNext()) {
829 final MoneyWiseSecurity mySecurity = myIterator.next();
830
831
832 boolean bIgnore = mySecurity.isClosed() || mySecurity.isDeleted();
833 bIgnore |= pClass != null && pClass.equals(mySecurity.getCategoryClass());
834 if (bIgnore) {
835 continue;
836 }
837
838
839 if (theMap.get(mySecurity.getIndexedId()) == null) {
840
841 final MoneyWiseSecurityHolding myHolding = new MoneyWiseSecurityHolding(thePortfolio, mySecurity);
842 myList.add(myHolding);
843 }
844 }
845
846
847 if (myList.isEmpty()) {
848 myList = null;
849 } else {
850 Collections.sort(myList);
851 }
852
853
854 return myList == null
855 ? null
856 : myList.iterator();
857 }
858 }
859 }