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.MoneyWiseTransCategory;
20  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
21  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQCategoryLineType;
22  import io.github.tonywasher.joceanus.moneywise.quicken.definitions.MoneyWiseQLineType;
23  import io.github.tonywasher.joceanus.moneywise.quicken.file.MoneyWiseQIFLine.MoneyWiseQIFStringLine;
24  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
25  
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Objects;
29  
30  /**
31   * Class representing a QIF Category record.
32   */
33  public class MoneyWiseQIFEventCategory
34          extends MoneyWiseQIFRecord<MoneyWiseQCategoryLineType>
35          implements Comparable<MoneyWiseQIFEventCategory> {
36      /**
37       * Item type.
38       */
39      protected static final String QIF_ITEM = "Cat";
40  
41      /**
42       * The Category Name.
43       */
44      private final String theName;
45  
46      /**
47       * The Category Description.
48       */
49      private final String theDesc;
50  
51      /**
52       * The Category Type.
53       */
54      private final boolean isIncome;
55  
56      /**
57       * Constructor.
58       *
59       * @param pCategory the Event Category
60       */
61      public MoneyWiseQIFEventCategory(final MoneyWiseTransCategory pCategory) {
62          /* Call super-constructor */
63          super(MoneyWiseQCategoryLineType.class);
64  
65          /* Store data */
66          theName = pCategory.getName();
67          theDesc = pCategory.getDesc();
68  
69          /* Determine whether this is an income category */
70          final MoneyWiseTransCategoryClass myClass = pCategory.getCategoryTypeClass();
71          isIncome = myClass.isIncome();
72  
73          /* Build lines */
74          addLine(new MoneyWiseQIFCategoryNameLine(theName));
75          if (theDesc != null) {
76              addLine(new MoneyWiseQIFCategoryDescLine(theDesc));
77          }
78          if (isIncome) {
79              addLine(new MoneyWiseQIFCategoryIncomeLine());
80          } else {
81              addLine(new MoneyWiseQIFCategoryExpenseLine());
82          }
83      }
84  
85      /**
86       * Constructor.
87       *
88       * @param pLines the data lines
89       */
90      protected MoneyWiseQIFEventCategory(final List<String> pLines) {
91          /* Call super-constructor */
92          super(MoneyWiseQCategoryLineType.class);
93  
94          /* Determine details */
95          String myName = null;
96          String myDesc = null;
97          boolean bIsIncome = false;
98  
99          /* Loop through the lines */
100         for (String myLine : pLines) {
101             /* Determine the category */
102             final MoneyWiseQCategoryLineType myType = MoneyWiseQCategoryLineType.parseLine(myLine);
103             if (myType != null) {
104                 /* Access data */
105                 final String myData = myLine.substring(myType.getSymbol().length());
106 
107                 /* Switch on line type */
108                 switch (myType) {
109                     case NAME:
110                         addLine(new MoneyWiseQIFCategoryNameLine(myData));
111                         myName = myData;
112                         break;
113                     case DESCRIPTION:
114                         addLine(new MoneyWiseQIFCategoryDescLine(myData));
115                         myDesc = myData;
116                         break;
117                     case INCOME:
118                         addLine(new MoneyWiseQIFCategoryIncomeLine());
119                         bIsIncome = true;
120                         break;
121                     case EXPENSE:
122                         addLine(new MoneyWiseQIFCategoryExpenseLine());
123                         bIsIncome = false;
124                         break;
125                     case TAX:
126                     default:
127                         break;
128                 }
129             }
130         }
131 
132         /* Build details */
133         theName = myName;
134         theDesc = myDesc;
135         isIncome = bIsIncome;
136     }
137 
138     @Override
139     public String toString() {
140         return getName();
141     }
142 
143     /**
144      * Obtain the Name.
145      *
146      * @return the Name
147      */
148     public String getName() {
149         return theName;
150     }
151 
152     /**
153      * Obtain the Description.
154      *
155      * @return the description
156      */
157     public String getDesc() {
158         return theDesc;
159     }
160 
161     /**
162      * Is the Category an income category.
163      *
164      * @return true/false
165      */
166     public boolean isIncome() {
167         return isIncome;
168     }
169 
170     /**
171      * Is the Category an expense category.
172      *
173      * @return true/false
174      */
175     public boolean isExpense() {
176         return !isIncome;
177     }
178 
179     @Override
180     public int compareTo(final MoneyWiseQIFEventCategory pThat) {
181         return theName.compareTo(pThat.getName());
182     }
183 
184     /**
185      * The Category Name line.
186      */
187     public static class MoneyWiseQIFCategoryNameLine
188             extends MoneyWiseQIFStringLine<MoneyWiseQCategoryLineType> {
189         /**
190          * Constructor.
191          *
192          * @param pName the Name
193          */
194         protected MoneyWiseQIFCategoryNameLine(final String pName) {
195             /* Call super-constructor */
196             super(pName);
197         }
198 
199         @Override
200         public MoneyWiseQCategoryLineType getLineType() {
201             return MoneyWiseQCategoryLineType.NAME;
202         }
203 
204         /**
205          * Obtain name.
206          *
207          * @return the name
208          */
209         public String getName() {
210             return getValue();
211         }
212     }
213 
214     /**
215      * The Category Description line.
216      */
217     public static class MoneyWiseQIFCategoryDescLine
218             extends MoneyWiseQIFStringLine<MoneyWiseQCategoryLineType> {
219         /**
220          * Constructor.
221          *
222          * @param pDesc the Description
223          */
224         protected MoneyWiseQIFCategoryDescLine(final String pDesc) {
225             /* Call super-constructor */
226             super(pDesc);
227         }
228 
229         @Override
230         public MoneyWiseQCategoryLineType getLineType() {
231             return MoneyWiseQCategoryLineType.DESCRIPTION;
232         }
233 
234         /**
235          * Obtain description.
236          *
237          * @return the description
238          */
239         public String getDescription() {
240             return getValue();
241         }
242     }
243 
244     /**
245      * The Category Income line.
246      */
247     public static class MoneyWiseQIFCategoryIncomeLine
248             extends MoneyWiseQIFLine<MoneyWiseQCategoryLineType> {
249         /**
250          * Constructor.
251          */
252         protected MoneyWiseQIFCategoryIncomeLine() {
253         }
254 
255         @Override
256         public MoneyWiseQCategoryLineType getLineType() {
257             return MoneyWiseQCategoryLineType.INCOME;
258         }
259 
260         @Override
261         public String toString() {
262             return getLineType().getSymbol();
263         }
264 
265         @Override
266         protected void formatData(final OceanusDataFormatter pFormatter,
267                                   final StringBuilder pBuilder) {
268             /* No data */
269         }
270     }
271 
272     /**
273      * The Category Expense line.
274      */
275     public static class MoneyWiseQIFCategoryExpenseLine
276             extends MoneyWiseQIFLine<MoneyWiseQCategoryLineType> {
277         /**
278          * Constructor.
279          */
280         protected MoneyWiseQIFCategoryExpenseLine() {
281         }
282 
283         @Override
284         public MoneyWiseQCategoryLineType getLineType() {
285             return MoneyWiseQCategoryLineType.EXPENSE;
286         }
287 
288         @Override
289         public String toString() {
290             return getLineType().getSymbol();
291         }
292 
293         @Override
294         protected void formatData(final OceanusDataFormatter pFormatter,
295                                   final StringBuilder pBuilder) {
296             /* No data */
297         }
298     }
299 
300     /**
301      * The Category Tax line.
302      */
303     public static class MoneyWiseQIFCategoryTaxLine
304             extends MoneyWiseQIFLine<MoneyWiseQCategoryLineType> {
305         /**
306          * Constructor.
307          */
308         protected MoneyWiseQIFCategoryTaxLine() {
309         }
310 
311         @Override
312         public MoneyWiseQCategoryLineType getLineType() {
313             return MoneyWiseQCategoryLineType.TAX;
314         }
315 
316         @Override
317         public String toString() {
318             return getLineType().getSymbol();
319         }
320 
321         @Override
322         protected void formatData(final OceanusDataFormatter pFormatter,
323                                   final StringBuilder pBuilder) {
324             /* No data */
325         }
326     }
327 
328     /**
329      * The Event Category line.
330      *
331      * @param <X> the line type
332      */
333     public abstract static class MoneyWiseQIFCategoryLine<X extends MoneyWiseQLineType>
334             extends MoneyWiseQIFLine<X> {
335         /**
336          * The event category.
337          */
338         private final MoneyWiseQIFEventCategory theCategory;
339 
340         /**
341          * The class list.
342          */
343         private final List<MoneyWiseQIFClass> theClasses;
344 
345         /**
346          * Constructor.
347          *
348          * @param pCategory the Event Category
349          */
350         protected MoneyWiseQIFCategoryLine(final MoneyWiseQIFEventCategory pCategory) {
351             this(pCategory, null);
352         }
353 
354         /**
355          * Constructor.
356          *
357          * @param pCategory the Event Category
358          * @param pClasses  the classes
359          */
360         protected MoneyWiseQIFCategoryLine(final MoneyWiseQIFEventCategory pCategory,
361                                            final List<MoneyWiseQIFClass> pClasses) {
362             /* Store data */
363             theCategory = pCategory;
364             theClasses = pClasses;
365         }
366 
367         @Override
368         public String toString() {
369             return theCategory.toString();
370         }
371 
372         /**
373          * Obtain event category.
374          *
375          * @return the event category
376          */
377         public MoneyWiseQIFEventCategory getEventCategory() {
378             return theCategory;
379         }
380 
381         /**
382          * Obtain class list.
383          *
384          * @return the class list
385          */
386         public List<MoneyWiseQIFClass> getClassList() {
387             return theClasses;
388         }
389 
390         @Override
391         protected void formatData(final OceanusDataFormatter pFormatter,
392                                   final StringBuilder pBuilder) {
393             /* Append the string data */
394             pBuilder.append(theCategory.getName());
395 
396             /* If we have classes */
397             if (theClasses != null) {
398                 /* Add class indicator */
399                 pBuilder.append(MoneyWiseQIFConstants.QIF_CLASS);
400 
401                 /* Iterate through the list */
402                 final Iterator<MoneyWiseQIFClass> myIterator = theClasses.iterator();
403                 while (myIterator.hasNext()) {
404                     final MoneyWiseQIFClass myClass = myIterator.next();
405 
406                     /* Add to the list */
407                     pBuilder.append(myClass.getName());
408                     if (myIterator.hasNext()) {
409                         pBuilder.append(MoneyWiseQIFConstants.QIF_CLASSSEP);
410                     }
411                 }
412             }
413         }
414 
415         @Override
416         public boolean equals(final Object pThat) {
417             /* Handle trivial cases */
418             if (this == pThat) {
419                 return true;
420             }
421             if (pThat == null) {
422                 return false;
423             }
424 
425             /* Check class */
426             if (!getClass().equals(pThat.getClass())) {
427                 return false;
428             }
429 
430             /* Cast correctly */
431             final MoneyWiseQIFCategoryLine<?> myLine = (MoneyWiseQIFCategoryLine<?>) pThat;
432 
433             /* Check line type */
434             if (!getLineType().equals(myLine.getLineType())) {
435                 return false;
436             }
437 
438             /* Check category */
439             if (!theCategory.equals(myLine.getEventCategory())) {
440                 return false;
441             }
442 
443             /* Check classes */
444             final List<MoneyWiseQIFClass> myClasses = myLine.getClassList();
445             if (theClasses == null) {
446                 return myClasses == null;
447             } else if (myClasses == null) {
448                 return true;
449             }
450             return theClasses.equals(myClasses);
451         }
452 
453         @Override
454         public int hashCode() {
455             return Objects.hash(getLineType(), theClasses, theCategory);
456         }
457     }
458 
459     /**
460      * The Event Category line.
461      *
462      * @param <X> the line type
463      */
464     public abstract static class MoneyWiseQIFCategoryAccountLine<X extends MoneyWiseQLineType>
465             extends MoneyWiseQIFLine<X> {
466         /**
467          * The event category.
468          */
469         private final MoneyWiseQIFEventCategory theCategory;
470 
471         /**
472          * The account.
473          */
474         private final MoneyWiseQIFAccount theAccount;
475 
476         /**
477          * The class list.
478          */
479         private final List<MoneyWiseQIFClass> theClasses;
480 
481         /**
482          * Constructor.
483          *
484          * @param pCategory the Event Category
485          * @param pAccount  the Account
486          */
487         protected MoneyWiseQIFCategoryAccountLine(final MoneyWiseQIFEventCategory pCategory,
488                                                   final MoneyWiseQIFAccount pAccount) {
489             this(pCategory, pAccount, null);
490         }
491 
492         /**
493          * Constructor.
494          *
495          * @param pCategory the Event Category
496          * @param pAccount  the Account
497          * @param pClasses  the classes
498          */
499         protected MoneyWiseQIFCategoryAccountLine(final MoneyWiseQIFEventCategory pCategory,
500                                                   final MoneyWiseQIFAccount pAccount,
501                                                   final List<MoneyWiseQIFClass> pClasses) {
502             /* Store data */
503             theCategory = pCategory;
504             theAccount = pAccount;
505             theClasses = pClasses;
506         }
507 
508         /**
509          * Obtain event category.
510          *
511          * @return the event category
512          */
513         public MoneyWiseQIFEventCategory getEventCategory() {
514             return theCategory;
515         }
516 
517         /**
518          * Obtain account.
519          *
520          * @return the account
521          */
522         public MoneyWiseQIFAccount getAccount() {
523             return theAccount;
524         }
525 
526         /**
527          * Obtain class list.
528          *
529          * @return the class list
530          */
531         public List<MoneyWiseQIFClass> getClassList() {
532             return theClasses;
533         }
534 
535         @Override
536         protected void formatData(final OceanusDataFormatter pFormatter,
537                                   final StringBuilder pBuilder) {
538             /* Append the string data */
539             pBuilder.append(theCategory.getName());
540             pBuilder.append(MoneyWiseQIFConstants.QIF_CATSEP);
541             pBuilder.append(MoneyWiseQIFConstants.QIF_XFERSTART);
542             pBuilder.append(theAccount.getName());
543             pBuilder.append(MoneyWiseQIFConstants.QIF_XFEREND);
544 
545             /* If we have classes */
546             if (theClasses != null) {
547                 /* Add class indicator */
548                 pBuilder.append(MoneyWiseQIFConstants.QIF_CLASS);
549 
550                 /* Iterate through the list */
551                 final Iterator<MoneyWiseQIFClass> myIterator = theClasses.iterator();
552                 while (myIterator.hasNext()) {
553                     final MoneyWiseQIFClass myClass = myIterator.next();
554 
555                     /* Add to the list */
556                     pBuilder.append(myClass.getName());
557                     if (myIterator.hasNext()) {
558                         pBuilder.append(MoneyWiseQIFConstants.QIF_CLASSSEP);
559                     }
560                 }
561             }
562         }
563 
564         @Override
565         public boolean equals(final Object pThat) {
566             /* Handle trivial cases */
567             if (this == pThat) {
568                 return true;
569             }
570             if (pThat == null) {
571                 return false;
572             }
573 
574             /* Check class */
575             if (!getClass().equals(pThat.getClass())) {
576                 return false;
577             }
578 
579             /* Cast correctly */
580             final MoneyWiseQIFCategoryAccountLine<?> myLine = (MoneyWiseQIFCategoryAccountLine<?>) pThat;
581 
582             /* Check line type */
583             if (!getLineType().equals(myLine.getLineType())) {
584                 return false;
585             }
586 
587             /* Check category */
588             if (!theCategory.equals(myLine.getEventCategory())) {
589                 return false;
590             }
591 
592             /* Check account */
593             if (!theAccount.equals(myLine.getAccount())) {
594                 return false;
595             }
596 
597             /* Check classes */
598             final List<MoneyWiseQIFClass> myClasses = myLine.getClassList();
599             if (theClasses == null) {
600                 return myClasses == null;
601             } else if (myClasses == null) {
602                 return true;
603             }
604             return theClasses.equals(myClasses);
605         }
606 
607         @Override
608         public int hashCode() {
609             return Objects.hash(getLineType(), theClasses, theAccount, theCategory);
610         }
611     }
612 }