View Javadoc
1   /*
2    * MoneyWise: Finance Application
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.moneywise.quicken.file;
18  
19  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
20  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseCash;
21  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
22  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseLoan;
23  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
25  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCashCategoryClass;
26  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseDepositCategoryClass;
27  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseLoanCategoryClass;
28  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQAccountLineType;
29  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQLineType;
30  import io.github.tonywasher.joceanus.moneywise.quicken.file.MoneyWiseQIFLine.MoneyWiseQIFMoneyLine;
31  import io.github.tonywasher.joceanus.moneywise.quicken.file.MoneyWiseQIFLine.MoneyWiseQIFStringLine;
32  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
33  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
34  
35  import java.util.HashMap;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Map.Entry;
40  import java.util.Objects;
41  
42  /**
43   * Class representing a QIF Account record.
44   */
45  public class MoneyWiseQIFAccount
46          extends MoneyWiseQIFRecord<MoneyWiseQAccountLineType>
47          implements Comparable<MoneyWiseQIFAccount> {
48      /**
49       * Item type.
50       */
51      protected static final String QIF_HDR = "!Account";
52  
53      /**
54       * Bank Account Type.
55       */
56      protected static final String QIFACT_BANK = "Bank";
57  
58      /**
59       * Cash Account Type.
60       */
61      protected static final String QIFACT_CASH = "Cash";
62  
63      /**
64       * Investment Account Type.
65       */
66      protected static final String QIFACT_INVST = "Invst";
67  
68      /**
69       * Credit Card Account Type.
70       */
71      protected static final String QIFACT_CCARD = "CCard";
72  
73      /**
74       * Asset Account Type.
75       */
76      protected static final String QIFACT_ASSET = "Oth A";
77  
78      /**
79       * Loan Account Type.
80       */
81      protected static final String QIFACT_LOAN = "Oth L";
82  
83      /**
84       * Category Map.
85       */
86      protected static final Map<Enum<?>, String> QIF_ACTCATMAP = createClassMap();
87  
88      /**
89       * The Account Name.
90       */
91      private final String theName;
92  
93      /**
94       * The Account Description.
95       */
96      private final String theDesc;
97  
98      /**
99       * The Account CategoryClass.
100      */
101     private final Enum<?> theClass;
102 
103     /**
104      * Constructor.
105      *
106      * @param pAccount the Account
107      */
108     public MoneyWiseQIFAccount(final MoneyWiseTransAsset pAccount) {
109         /* Call super-constructor */
110         super(MoneyWiseQAccountLineType.class);
111 
112         /* Store data */
113         theName = pAccount.getName();
114 
115         /* Switch on account */
116         switch (pAccount) {
117             case MoneyWiseDeposit myDeposit -> {
118                 theClass = myDeposit.getCategoryClass();
119                 theDesc = myDeposit.getDesc();
120             }
121             case MoneyWiseCash myCash -> {
122                 theClass = myCash.getCategoryClass();
123                 theDesc = myCash.getDesc();
124             }
125             case MoneyWiseLoan myLoan -> {
126                 theClass = myLoan.getCategoryClass();
127                 theDesc = myLoan.getDesc();
128             }
129             case MoneyWisePortfolio myPortfolio -> {
130                 theClass = MoneyWiseBasicDataType.PORTFOLIO;
131                 theDesc = myPortfolio.getDesc();
132             }
133             default -> throw new IllegalArgumentException();
134         }
135 
136         /* Build lines */
137         addLine(new MoneyWiseQIFAccountNameLine(theName));
138         if (theDesc != null) {
139             addLine(new MoneyWiseQIFAccountDescLine(theDesc));
140         }
141         addLine(new MoneyWiseQIFAccountTypeLine(theClass));
142     }
143 
144     /**
145      * Constructor for holding account.
146      *
147      * @param pName the Portfolio Name
148      */
149     protected MoneyWiseQIFAccount(final String pName) {
150         /* Call super-constructor */
151         super(MoneyWiseQAccountLineType.class);
152 
153         /* Store data */
154         theName = pName;
155         theClass = MoneyWiseDepositCategoryClass.SAVINGS;
156         theDesc = null;
157 
158         /* Build lines */
159         addLine(new MoneyWiseQIFAccountNameLine(theName));
160         addLine(new MoneyWiseQIFAccountTypeLine(theClass));
161     }
162 
163     /**
164      * Constructor.
165      *
166      * @param pFormatter the formatter
167      * @param pLines     the data lines
168      */
169     protected MoneyWiseQIFAccount(final OceanusDataFormatter pFormatter,
170                                   final List<String> pLines) {
171         /* Call super-constructor */
172         super(MoneyWiseQAccountLineType.class);
173 
174         /* Determine details */
175         String myName = null;
176         String myDesc = null;
177         Enum<?> myClass = null;
178 
179         /* Loop through the lines */
180         for (String myLine : pLines) {
181             /* Determine the category */
182             final MoneyWiseQAccountLineType myType = MoneyWiseQAccountLineType.parseLine(myLine);
183             if (myType != null) {
184                 /* Access data */
185                 final String myData = myLine.substring(myType.getSymbol().length());
186 
187                 /* Switch on line type */
188                 switch (myType) {
189                     case NAME:
190                         addLine(new MoneyWiseQIFAccountNameLine(myData));
191                         myName = myData;
192                         break;
193                     case DESCRIPTION:
194                         addLine(new MoneyWiseQIFAccountDescLine(myData));
195                         myDesc = myData;
196                         break;
197                     case TYPE:
198                         final MoneyWiseQIFAccountTypeLine myQLine = new MoneyWiseQIFAccountTypeLine(myData);
199                         addLine(myQLine);
200                         myClass = myQLine.getAccountClass();
201                         break;
202                     case CREDITLIMIT:
203                         final OceanusMoney myMoney = pFormatter.getDecimalParser().parseMoneyValue(myData);
204                         addLine(new MoneyWiseQIFAccountLimitLine(myMoney));
205                         break;
206                     default:
207                         break;
208                 }
209             }
210         }
211 
212         /* Build details */
213         theName = myName;
214         theDesc = myDesc;
215         theClass = myClass;
216     }
217 
218     @Override
219     public String toString() {
220         return getName();
221     }
222 
223     /**
224      * Obtain the Name.
225      *
226      * @return the Name
227      */
228     public String getName() {
229         return theName;
230     }
231 
232     /**
233      * Obtain the description.
234      *
235      * @return the Name
236      */
237     public String getDesc() {
238         return theDesc;
239     }
240 
241     /**
242      * Obtain the account type.
243      *
244      * @return the Type
245      */
246     public String getType() {
247         return QIF_ACTCATMAP.get(theClass);
248     }
249 
250     /**
251      * Create the CategoryClass to type map.
252      *
253      * @return the map
254      */
255     private static Map<Enum<?>, String> createClassMap() {
256         /* Create the map */
257         final Map<Enum<?>, String> myMap = new HashMap<>();
258 
259         /* Add the entries */
260         myMap.put(MoneyWiseDepositCategoryClass.CHECKING, QIFACT_BANK);
261         myMap.put(MoneyWiseDepositCategoryClass.SAVINGS, QIFACT_BANK);
262         myMap.put(MoneyWiseDepositCategoryClass.PEER2PEER, QIFACT_BANK);
263         myMap.put(MoneyWiseDepositCategoryClass.BOND, QIFACT_BANK);
264         myMap.put(MoneyWiseCashCategoryClass.CASH, QIFACT_CASH);
265         myMap.put(MoneyWiseCashCategoryClass.AUTOEXPENSE, QIFACT_CASH);
266         myMap.put(MoneyWiseLoanCategoryClass.CREDITCARD, QIFACT_CCARD);
267         myMap.put(MoneyWiseBasicDataType.PORTFOLIO, QIFACT_INVST);
268         myMap.put(MoneyWiseLoanCategoryClass.PRIVATELOAN, QIFACT_ASSET);
269         myMap.put(MoneyWiseLoanCategoryClass.LOAN, QIFACT_LOAN);
270 
271         /* Return the map */
272         return myMap;
273     }
274 
275     @Override
276     public int compareTo(final MoneyWiseQIFAccount pThat) {
277         return theName.compareTo(pThat.getName());
278     }
279 
280     /**
281      * The Account Name line.
282      */
283     public static class MoneyWiseQIFAccountNameLine
284             extends MoneyWiseQIFStringLine<MoneyWiseQAccountLineType> {
285         /**
286          * Constructor.
287          *
288          * @param pName the Name
289          */
290         protected MoneyWiseQIFAccountNameLine(final String pName) {
291             /* Call super-constructor */
292             super(pName);
293         }
294 
295         @Override
296         public MoneyWiseQAccountLineType getLineType() {
297             return MoneyWiseQAccountLineType.NAME;
298         }
299 
300         /**
301          * Obtain name.
302          *
303          * @return the name
304          */
305         public String getName() {
306             return getValue();
307         }
308     }
309 
310     /**
311      * The Security Symbol line.
312      */
313     public static class MoneyWiseQIFAccountDescLine
314             extends MoneyWiseQIFStringLine<MoneyWiseQAccountLineType> {
315         /**
316          * Constructor.
317          *
318          * @param pDesc the Description
319          */
320         protected MoneyWiseQIFAccountDescLine(final String pDesc) {
321             /* Call super-constructor */
322             super(pDesc);
323         }
324 
325         @Override
326         public MoneyWiseQAccountLineType getLineType() {
327             return MoneyWiseQAccountLineType.DESCRIPTION;
328         }
329 
330         /**
331          * Obtain description.
332          *
333          * @return the description
334          */
335         public String getDesc() {
336             return getValue();
337         }
338     }
339 
340     /**
341      * The Account Type line.
342      */
343     public static class MoneyWiseQIFAccountTypeLine
344             extends MoneyWiseQIFStringLine<MoneyWiseQAccountLineType> {
345         /**
346          * The Account Category Class.
347          */
348         private final Enum<?> theClass;
349 
350         /**
351          * Constructor.
352          *
353          * @param pClass the Account Class
354          */
355         protected MoneyWiseQIFAccountTypeLine(final Enum<?> pClass) {
356             /* Call super-constructor */
357             super(QIF_ACTCATMAP.get(pClass));
358 
359             /* Record the class */
360             theClass = pClass;
361         }
362 
363         /**
364          * Constructor.
365          *
366          * @param pType the Account Type
367          */
368         protected MoneyWiseQIFAccountTypeLine(final String pType) {
369             /* Call super-constructor */
370             super(pType);
371 
372             /* Loop through the map entries */
373             Enum<?> myClass = null;
374             for (Entry<Enum<?>, String> myEntry : QIF_ACTCATMAP.entrySet()) {
375                 /* If we have a match */
376                 if (pType.equals(myEntry.getValue())) {
377                     myClass = myEntry.getKey();
378                     break;
379                 }
380             }
381 
382             /* Store the class */
383             theClass = myClass;
384         }
385 
386         @Override
387         public MoneyWiseQAccountLineType getLineType() {
388             return MoneyWiseQAccountLineType.TYPE;
389         }
390 
391         @Override
392         public String toString() {
393             return theClass.toString();
394         }
395 
396         /**
397          * Obtain account class.
398          *
399          * @return the account class
400          */
401         public Enum<?> getAccountClass() {
402             return theClass;
403         }
404     }
405 
406     /**
407      * The Account Credit Limit line.
408      */
409     public static class MoneyWiseQIFAccountLimitLine
410             extends MoneyWiseQIFMoneyLine<MoneyWiseQAccountLineType> {
411         /**
412          * Constructor.
413          *
414          * @param pLimit the Credit Limit
415          */
416         protected MoneyWiseQIFAccountLimitLine(final OceanusMoney pLimit) {
417             /* Call super-constructor */
418             super(pLimit);
419         }
420 
421         @Override
422         public MoneyWiseQAccountLineType getLineType() {
423             return MoneyWiseQAccountLineType.CREDITLIMIT;
424         }
425 
426         /**
427          * Obtain credit limit.
428          *
429          * @return the credit limit
430          */
431         public OceanusMoney getCreditLimit() {
432             return getMoney();
433         }
434     }
435 
436     /**
437      * The Account line.
438      *
439      * @param <X> the line type
440      */
441     public abstract static class MoneyWiseQIFXferAccountLine<X extends MoneyWiseQLineType>
442             extends MoneyWiseQIFLine<X> {
443         /**
444          * The account.
445          */
446         private final MoneyWiseQIFAccount theAccount;
447 
448         /**
449          * The class list.
450          */
451         private final List<MoneyWiseQIFClass> theClasses;
452 
453         /**
454          * Constructor.
455          *
456          * @param pAccount the Account
457          */
458         protected MoneyWiseQIFXferAccountLine(final MoneyWiseQIFAccount pAccount) {
459             this(pAccount, null);
460         }
461 
462         /**
463          * Constructor.
464          *
465          * @param pAccount the Account
466          * @param pClasses the classes
467          */
468         protected MoneyWiseQIFXferAccountLine(final MoneyWiseQIFAccount pAccount,
469                                               final List<MoneyWiseQIFClass> pClasses) {
470             /* Store data */
471             theAccount = pAccount;
472             theClasses = pClasses;
473         }
474 
475         @Override
476         public String toString() {
477             return theAccount.toString();
478         }
479 
480         /**
481          * Obtain account.
482          *
483          * @return the account
484          */
485         public MoneyWiseQIFAccount getAccount() {
486             return theAccount;
487         }
488 
489         /**
490          * Obtain class list.
491          *
492          * @return the class list
493          */
494         public List<MoneyWiseQIFClass> getClassList() {
495             return theClasses;
496         }
497 
498         @Override
499         protected void formatData(final OceanusDataFormatter pFormatter,
500                                   final StringBuilder pBuilder) {
501             /* Append the string data */
502             pBuilder.append(MoneyWiseQIFConstants.QIF_XFERSTART);
503             pBuilder.append(theAccount.getName());
504             pBuilder.append(MoneyWiseQIFConstants.QIF_XFEREND);
505 
506             /* If we have classes */
507             if (theClasses != null) {
508                 /* Add class indicator */
509                 pBuilder.append(MoneyWiseQIFConstants.QIF_CLASS);
510 
511                 /* Iterate through the list */
512                 final Iterator<MoneyWiseQIFClass> myIterator = theClasses.iterator();
513                 while (myIterator.hasNext()) {
514                     final MoneyWiseQIFClass myClass = myIterator.next();
515 
516                     /* Add to the list */
517                     pBuilder.append(myClass.getName());
518                     if (myIterator.hasNext()) {
519                         pBuilder.append(MoneyWiseQIFConstants.QIF_CLASSSEP);
520                     }
521                 }
522             }
523         }
524 
525         @Override
526         public boolean equals(final Object pThat) {
527             /* Handle trivial cases */
528             if (this == pThat) {
529                 return true;
530             }
531             if (pThat == null) {
532                 return false;
533             }
534 
535             /* Check class */
536             if (!getClass().equals(pThat.getClass())) {
537                 return false;
538             }
539 
540             /* Cast correctly */
541             final MoneyWiseQIFXferAccountLine<?> myLine = (MoneyWiseQIFXferAccountLine<?>) pThat;
542 
543             /* Check line type */
544             if (!getLineType().equals(myLine.getLineType())) {
545                 return false;
546             }
547 
548             /* Check account */
549             if (!theAccount.equals(myLine.getAccount())) {
550                 return false;
551             }
552 
553             /* Check classes */
554             final List<MoneyWiseQIFClass> myClasses = myLine.getClassList();
555             if (theClasses == null) {
556                 return myClasses == null;
557             } else if (myClasses == null) {
558                 return true;
559             }
560             return theClasses.equals(myClasses);
561         }
562 
563         @Override
564         public int hashCode() {
565             return Objects.hash(getLineType(), theClasses, theAccount);
566         }
567     }
568 }