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.quicken.definitions.MoneyWiseQIFType;
20  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
21  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
22  
23  import java.io.BufferedReader;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  /**
29   * Class to parse a QIFFile.
30   */
31  public class MoneyWiseQIFParser {
32      /**
33       * The QIFFile being built.
34       */
35      private final MoneyWiseQIFFile theFile;
36  
37      /**
38       * Data formatter.
39       */
40      private final OceanusDataFormatter theFormatter;
41  
42      /**
43       * Record mode.
44       */
45      private MoneyWiseQIFSection theSection;
46  
47      /**
48       * Active account.
49       */
50      private MoneyWiseQIFAccountEvents theActive;
51  
52      /**
53       * is active account portfolio?
54       */
55      private boolean isPortfolio;
56  
57      /**
58       * Constructor.
59       *
60       * @param pFactory  the gui factory
61       * @param pFileType the QIF file type.
62       */
63      public MoneyWiseQIFParser(final TethysUIFactory<?> pFactory,
64                                final MoneyWiseQIFType pFileType) {
65          /* Create new file */
66          theFile = new MoneyWiseQIFFile(pFileType);
67  
68          /* Allocate the formatter and set date format */
69          theFormatter = pFactory.newDataFormatter();
70          theFormatter.setFormat(MoneyWiseQIFWriter.QIF_DATEFORMAT);
71      }
72  
73      /**
74       * Obtain file.
75       *
76       * @return the QIF file.
77       */
78      public MoneyWiseQIFFile getFile() {
79          return theFile;
80      }
81  
82      /**
83       * Load from Stream.
84       *
85       * @param pStream the input stream
86       * @return continue true/false
87       * @throws IOException on error
88       */
89      public boolean loadFile(final BufferedReader pStream) throws IOException {
90          /* List of lines */
91          final List<String> myLines = new ArrayList<>();
92  
93          /* Loop through the file */
94          while (true) {
95              /* Read the next line and break on EOF */
96              final String myLine = pStream.readLine();
97              if (myLine == null) {
98                  break;
99              }
100 
101             /* If this is a command */
102             if (myLine.startsWith(MoneyWiseQIFRecord.QIF_CMD)) {
103                 /* Process the mode */
104                 processMode(myLine);
105                 /* If this is an EOI */
106             } else if (myLine.equals(MoneyWiseQIFRecord.QIF_EOI)) {
107                 /* Process the records */
108                 processRecord(myLines);
109 
110                 /* else normal line */
111             } else {
112                 /* Ignore blank lines */
113                 if (myLine.length() > 0) {
114                     myLines.add(myLine);
115                 }
116             }
117         }
118 
119         /* Sort the file lists */
120         theFile.sortLists();
121 
122         /* Return to the caller */
123         return true;
124     }
125 
126     /**
127      * Process the mode.
128      *
129      * @param pLine the line to process
130      */
131     private void processMode(final String pLine) {
132         /* If the line is a type record */
133         if (pLine.startsWith(MoneyWiseQIFRecord.QIF_ITEMTYPE)) {
134             /* Access the type */
135             final String myType = pLine.substring(MoneyWiseQIFRecord.QIF_ITEMTYPE.length());
136 
137             /* Determine which section this describes */
138             final MoneyWiseQIFSection mySection = MoneyWiseQIFSection.determineType(myType);
139 
140             /* If we found a section */
141             if (mySection != null) {
142                 /* Switch on the section */
143                 switch (mySection) {
144                     case CLASS:
145                     case CATEGORY:
146                     case SECURITY:
147                     case PRICE:
148                         theSection = mySection;
149                         theActive = null;
150                         break;
151                     default:
152                         theSection = null;
153                         break;
154                 }
155 
156                 /* else if we have an active account */
157             } else if (theActive != null) {
158                 /* Make sure that the type matches */
159                 final String myActiveType = theActive.getAccount().getType();
160                 if (myActiveType.equals(myType)) {
161                     /* Set events */
162                     theSection = MoneyWiseQIFSection.EVENT;
163 
164                     /* Note portfolio */
165                     isPortfolio = myType.equals(MoneyWiseQIFAccount.QIFACT_INVST);
166 
167                     /* Not recognised */
168                 } else {
169                     theSection = null;
170                 }
171 
172                 /* Not recognised */
173             } else {
174                 theSection = null;
175             }
176 
177             /* Handle Account call */
178         } else if (pLine.startsWith(MoneyWiseQIFAccount.QIF_HDR)) {
179             /* Look for account record */
180             theSection = MoneyWiseQIFSection.ACCOUNT;
181         }
182     }
183 
184     /**
185      * Process the record.
186      *
187      * @param pLines the lines to process
188      */
189     private void processRecord(final List<String> pLines) {
190         /* Switch on the section */
191         switch (theSection) {
192             case CLASS:
193                 processClassRecord(pLines);
194                 break;
195             case CATEGORY:
196                 processCategoryRecord(pLines);
197                 break;
198             case ACCOUNT:
199                 processAccountRecord(pLines);
200                 break;
201             case SECURITY:
202                 processSecurityRecord(pLines);
203                 break;
204             case EVENT:
205                 processEventRecord(pLines);
206                 break;
207             case PRICE:
208                 processPriceRecord(pLines);
209                 break;
210             default:
211                 break;
212         }
213 
214         /* Clear line list */
215         pLines.clear();
216     }
217 
218     /**
219      * Process the class record.
220      *
221      * @param pLines the lines to process
222      */
223     private void processClassRecord(final List<String> pLines) {
224         /* register the class */
225         final MoneyWiseQIFClass myClass = new MoneyWiseQIFClass(theFile, pLines);
226         theFile.registerClass(myClass);
227     }
228 
229     /**
230      * Process the category record.
231      *
232      * @param pLines the lines to process
233      */
234     private void processCategoryRecord(final List<String> pLines) {
235         /* Register the category */
236         final MoneyWiseQIFEventCategory myCategory = new MoneyWiseQIFEventCategory(theFile, pLines);
237         theFile.registerCategory(myCategory);
238     }
239 
240     /**
241      * Process the account record.
242      *
243      * @param pLines the lines to process
244      */
245     private void processAccountRecord(final List<String> pLines) {
246         /* Register the account */
247         final MoneyWiseQIFAccount myAccount = new MoneyWiseQIFAccount(theFile, theFormatter, pLines);
248         theActive = theFile.registerAccount(myAccount);
249     }
250 
251     /**
252      * Process the security record.
253      *
254      * @param pLines the lines to process
255      */
256     private void processSecurityRecord(final List<String> pLines) {
257         /* Register the security */
258         final MoneyWiseQIFSecurity mySecurity = new MoneyWiseQIFSecurity(theFile, pLines);
259         theFile.registerSecurity(mySecurity);
260     }
261 
262     /**
263      * Process the event record.
264      *
265      * @param pLines the lines to process
266      */
267     private void processEventRecord(final List<String> pLines) {
268         /* Switch on portfolio */
269         if (isPortfolio) {
270             /* Register the event */
271             final MoneyWiseQIFPortfolioEvent myEvent = new MoneyWiseQIFPortfolioEvent(theFile, theFormatter, pLines);
272             theActive.addEvent(myEvent);
273         } else {
274             /* Register the event */
275             final MoneyWiseQIFEvent myEvent = new MoneyWiseQIFEvent(theFile, theFormatter, pLines);
276             theActive.addEvent(myEvent);
277         }
278     }
279 
280     /**
281      * Process the price record.
282      *
283      * @param pLines the lines to process
284      */
285     private void processPriceRecord(final List<String> pLines) {
286         /* Register the price */
287         final MoneyWiseQIFPrice myPrice = new MoneyWiseQIFPrice(theFile, theFormatter, pLines);
288         theFile.registerPrice(myPrice);
289     }
290 
291     /**
292      * QIF File section.
293      */
294     private enum MoneyWiseQIFSection {
295         /**
296          * Classes.
297          */
298         CLASS("Class"),
299 
300         /**
301          * Category.
302          */
303         CATEGORY("Cat"),
304 
305         /**
306          * Account.
307          */
308         ACCOUNT("Account"),
309 
310         /**
311          * Security.
312          */
313         SECURITY("Security"),
314 
315         /**
316          * EVENT.
317          */
318         EVENT(null),
319 
320         /**
321          * Price.
322          */
323         PRICE("Prices");
324 
325         /**
326          * Type.
327          */
328         private final String theType;
329 
330         /**
331          * Constructor.
332          *
333          * @param pType the type
334          */
335         MoneyWiseQIFSection(final String pType) {
336             theType = pType;
337         }
338 
339         /**
340          * Determine section.
341          *
342          * @param pLine the line to check
343          * @return the section
344          */
345         private static MoneyWiseQIFSection determineType(final String pLine) {
346             /* Loop through the values */
347             for (MoneyWiseQIFSection mySection : MoneyWiseQIFSection.values()) {
348                 /* If we have a match */
349                 if (pLine.equals(mySection.theType)) {
350                     return mySection;
351                 }
352             }
353 
354             /* Not found */
355             return null;
356         }
357     }
358 }