1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  package org.kuali.ole.sys.businessobject;
18  
19  import static org.kuali.ole.sys.OLEKeyConstants.AccountingLineParser.ERROR_INVALID_FILE_FORMAT;
20  import static org.kuali.ole.sys.OLEKeyConstants.AccountingLineParser.ERROR_INVALID_PROPERTY_VALUE;
21  import static org.kuali.ole.sys.OLEPropertyConstants.ACCOUNT_NUMBER;
22  import static org.kuali.ole.sys.OLEPropertyConstants.AMOUNT;
23  import static org.kuali.ole.sys.OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE;
24  import static org.kuali.ole.sys.OLEPropertyConstants.FINANCIAL_OBJECT_CODE;
25  import static org.kuali.ole.sys.OLEPropertyConstants.FINANCIAL_SUB_OBJECT_CODE;
26  import static org.kuali.ole.sys.OLEPropertyConstants.ORGANIZATION_REFERENCE_ID;
27  import static org.kuali.ole.sys.OLEPropertyConstants.OVERRIDE_CODE;
28  import static org.kuali.ole.sys.OLEPropertyConstants.POSTING_YEAR;
29  import static org.kuali.ole.sys.OLEPropertyConstants.PROJECT_CODE;
30  import static org.kuali.ole.sys.OLEPropertyConstants.SEQUENCE_NUMBER;
31  import static org.kuali.ole.sys.OLEPropertyConstants.SUB_ACCOUNT_NUMBER;
32  
33  import java.io.BufferedReader;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.InputStreamReader;
37  import java.lang.reflect.InvocationTargetException;
38  import java.util.ArrayList;
39  import java.util.HashMap;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Map.Entry;
43  
44  import org.apache.commons.lang.StringUtils;
45  import org.kuali.ole.coa.service.AccountService;
46  import org.kuali.ole.sys.OLEConstants;
47  import org.kuali.ole.sys.OLEKeyConstants;
48  import org.kuali.ole.sys.OLEPropertyConstants;
49  import org.kuali.ole.sys.context.SpringContext;
50  import org.kuali.ole.sys.document.AccountingDocument;
51  import org.kuali.ole.sys.exception.AccountingLineParserException;
52  import org.kuali.rice.core.web.format.FormatException;
53  import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
54  import org.kuali.rice.kns.service.DataDictionaryService;
55  import org.kuali.rice.krad.util.GlobalVariables;
56  import org.kuali.rice.krad.util.ObjectUtils;
57  
58  
59  
60  
61  public class AccountingLineParserBase implements AccountingLineParser {
62      protected static final String[] DEFAULT_FORMAT = { CHART_OF_ACCOUNTS_CODE, ACCOUNT_NUMBER, SUB_ACCOUNT_NUMBER, FINANCIAL_OBJECT_CODE, FINANCIAL_SUB_OBJECT_CODE, PROJECT_CODE, ORGANIZATION_REFERENCE_ID, AMOUNT };
63      private String fileName;
64      private Integer lineNo = 0;
65  
66      
67  
68  
69      public String[] getSourceAccountingLineFormat() {
70          return removeChartFromFormatIfNeeded(DEFAULT_FORMAT);
71      }
72  
73      
74  
75  
76      public String[] getTargetAccountingLineFormat() {
77          return removeChartFromFormatIfNeeded(DEFAULT_FORMAT);
78      }
79  
80      
81  
82  
83  
84      public String[] removeChartFromFormatIfNeeded(String[] format) {
85          if (SpringContext.getBean(AccountService.class).accountsCanCrossCharts()) {
86              return format;
87          }
88          
89          
90          String[] formatNoChart = new String[format.length-1];
91          int idx = 0;
92          for (int i=0; i<format.length; i++) {
93              if (format[i].equals(CHART_OF_ACCOUNTS_CODE)) 
94                  continue;
95              else {
96                  formatNoChart[idx] = format[i];
97                  idx++;
98              }
99          }
100         return formatNoChart;
101     }
102     
103     
104 
105 
106     public String getExpectedAccountingLineFormatAsString(Class<? extends AccountingLine> accountingLineClass) {
107         StringBuffer sb = new StringBuffer();
108         boolean first = true;
109         for (String attributeName : chooseFormat(accountingLineClass)) {
110             if (!first) {
111                 sb.append(",");
112             }
113             else {
114                 first = false;
115             }
116             sb.append(retrieveAttributeLabel(accountingLineClass, attributeName));
117         }
118         return sb.toString();
119     }
120 
121     
122 
123 
124 
125     public SourceAccountingLine parseSourceAccountingLine(AccountingDocument transactionalDocument, String sourceAccountingLineString) {
126         Class sourceAccountingLineClass = getSourceAccountingLineClass(transactionalDocument);
127         SourceAccountingLine sourceAccountingLine = (SourceAccountingLine) populateAccountingLine(transactionalDocument, sourceAccountingLineClass, sourceAccountingLineString, parseAccountingLine(sourceAccountingLineClass, sourceAccountingLineString), transactionalDocument.getNextSourceLineNumber());
128         return sourceAccountingLine;
129     }
130     
131     
132 
133 
134 
135 
136     protected Class getSourceAccountingLineClass(final AccountingDocument accountingDocument) {
137         return accountingDocument.getSourceAccountingLineClass();
138     }
139 
140     
141 
142 
143 
144     public TargetAccountingLine parseTargetAccountingLine(AccountingDocument transactionalDocument, String targetAccountingLineString) {
145         Class targetAccountingLineClass = getTargetAccountingLineClass(transactionalDocument);
146         TargetAccountingLine targetAccountingLine = (TargetAccountingLine) populateAccountingLine(transactionalDocument, targetAccountingLineClass, targetAccountingLineString, parseAccountingLine(targetAccountingLineClass, targetAccountingLineString), transactionalDocument.getNextTargetLineNumber());
147         return targetAccountingLine;
148     }
149     
150     
151 
152 
153 
154 
155     protected Class getTargetAccountingLineClass(final AccountingDocument accountingDocument) {
156         return accountingDocument.getTargetAccountingLineClass();
157     }
158 
159     
160 
161 
162 
163 
164 
165 
166 
167 
168 
169     protected AccountingLine populateAccountingLine(AccountingDocument transactionalDocument, Class<? extends AccountingLine> accountingLineClass, String accountingLineAsString, Map<String, String> attributeValueMap, Integer sequenceNumber) {
170 
171         putCommonAttributesInMap(attributeValueMap, transactionalDocument, sequenceNumber);
172 
173         
174         AccountingLine accountingLine;
175 
176         try {
177             accountingLine = (AccountingLine) accountingLineClass.newInstance();
178             
179             
180             if (SourceAccountingLine.class.isAssignableFrom(accountingLineClass)) {
181                 performCustomSourceAccountingLinePopulation(attributeValueMap, (SourceAccountingLine) accountingLine, accountingLineAsString);
182             }
183             else if (TargetAccountingLine.class.isAssignableFrom(accountingLineClass)) {
184                 performCustomTargetAccountingLinePopulation(attributeValueMap, (TargetAccountingLine) accountingLine, accountingLineAsString);
185             }
186             else {
187                 throw new IllegalArgumentException("invalid (unknown) accounting line type: " + accountingLineClass);
188             }
189             
190             for (Entry<String, String> entry : attributeValueMap.entrySet()) {
191                 try {
192                     try {
193                         Class entryType = ObjectUtils.easyGetPropertyType(accountingLine, entry.getKey());
194                         if (String.class.isAssignableFrom(entryType)) {
195                             entry.setValue(entry.getValue().toUpperCase());
196                         }
197                         ObjectUtils.setObjectProperty(accountingLine, entry.getKey(), entryType, entry.getValue());
198                     }
199                     catch (IllegalArgumentException e) {
200                         throw new IllegalArgumentException("unable to complete accounting line population.", e);
201                     }
202                 }
203                 catch (FormatException e) {
204                     String[] errorParameters = { entry.getValue().toString(), retrieveAttributeLabel(accountingLine.getClass(), entry.getKey()), accountingLineAsString };
205                     
206                     GlobalVariables.getMessageMap().putError(OLEConstants.ACCOUNTING_LINE_ERRORS, ERROR_INVALID_PROPERTY_VALUE, entry.getValue().toString(), entry.getKey(), accountingLineAsString + "  : Line Number " + lineNo.toString());
207                     throw new AccountingLineParserException("invalid '" + entry.getKey() + "=" + entry.getValue() + "for " + accountingLineAsString, ERROR_INVALID_PROPERTY_VALUE, errorParameters);
208                 }
209             }
210             
211             
212             SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(accountingLine);            
213         }
214         catch (SecurityException e) {
215             throw new IllegalArgumentException("unable to complete accounting line population.", e);
216         }
217         catch (NoSuchMethodException e) {
218             throw new IllegalArgumentException("unable to complete accounting line population.", e);
219         }
220         catch (IllegalAccessException e) {
221             throw new IllegalArgumentException("unable to complete accounting line population.", e);
222         }
223         catch (InvocationTargetException e) {
224             throw new IllegalArgumentException("unable to complete accounting line population.", e);
225         }
226         catch (InstantiationException e) {
227             throw new IllegalArgumentException("unable to complete accounting line population.", e);
228         }
229 
230         
231         SpringContext.getBean(BusinessObjectDictionaryService.class).performForceUppercase(accountingLine);
232         accountingLine.refresh();
233 
234         return accountingLine;
235     }
236 
237     
238 
239 
240 
241 
242 
243 
244     protected void putCommonAttributesInMap(Map<String, String> attributeValueMap, AccountingDocument document, Integer sequenceNumber) {
245         attributeValueMap.put(OLEPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
246         attributeValueMap.put(POSTING_YEAR, document.getPostingYear().toString());
247         attributeValueMap.put(SEQUENCE_NUMBER, sequenceNumber.toString());
248     }
249 
250     
251 
252 
253 
254 
255 
256 
257     protected Map<String, String> parseAccountingLine(Class<? extends AccountingLine> accountingLineClass, String lineToParse) {
258         if (StringUtils.isNotBlank(fileName) && !StringUtils.lowerCase(fileName).endsWith(".csv")) {
259             throw new AccountingLineParserException("unsupported file format: " + fileName, ERROR_INVALID_FILE_FORMAT, fileName);
260         }
261         String[] attributes = chooseFormat(accountingLineClass);
262         String[] attributeValues = StringUtils.splitPreserveAllTokens(lineToParse, ",");
263 
264         Map<String, String> attributeValueMap = new HashMap<String, String>();
265 
266         for (int i = 0; i < Math.min(attributeValues.length, attributes.length); i++) {
267             attributeValueMap.put(attributes[i], attributeValues[i]);
268         }
269 
270         return attributeValueMap;
271     }
272 
273     
274 
275 
276 
277 
278 
279 
280     protected void performCustomSourceAccountingLinePopulation(Map<String, String> attributeValueMap, SourceAccountingLine sourceAccountingLine, String accountingLineAsString) {
281     }
282 
283     
284 
285 
286 
287 
288 
289 
290     protected void performCustomTargetAccountingLinePopulation(Map<String, String> attributeValueMap, TargetAccountingLine targetAccountingLine, String accountingLineAsString) {
291     }
292 
293     
294 
295 
296 
297 
298 
299 
300 
301     protected List<AccountingLine> importAccountingLines(String fileName, InputStream stream, AccountingDocument transactionalDocument, boolean isSource) {
302         List<AccountingLine> importedAccountingLines = new ArrayList<AccountingLine>();
303         this.fileName = fileName;
304         BufferedReader br = new BufferedReader(new InputStreamReader(stream));
305 
306         try {
307             String accountingLineAsString = null;
308             lineNo = 0;
309             while ((accountingLineAsString = br.readLine()) != null) {
310                 lineNo++;
311                 
312                 if (StringUtils.isBlank(StringUtils.remove(StringUtils.deleteWhitespace(accountingLineAsString),OLEConstants.COMMA))) {
313                     continue;
314                 }
315                 
316                 AccountingLine accountingLine = null;
317 
318                 try {
319                     if (isSource) {
320                         accountingLine = parseSourceAccountingLine(transactionalDocument, accountingLineAsString);
321                     }
322                     else {
323                         accountingLine = parseTargetAccountingLine(transactionalDocument, accountingLineAsString);
324                     }
325 
326                     validateImportedAccountingLine(accountingLine, accountingLineAsString);
327                     importedAccountingLines.add(accountingLine);
328                 }
329                 catch (AccountingLineParserException e) {
330                     GlobalVariables.getMessageMap().putError((isSource ? "sourceAccountingLines" : "targetAccountingLines"), OLEKeyConstants.ERROR_ACCOUNTING_DOCUMENT_ACCOUNTING_LINE_IMPORT_GENERAL, new String[] { e.getMessage() });
331                 }
332             }
333         }
334         catch (IOException e) {
335             throw new IllegalArgumentException("unable to readLine from bufferReader in accountingLineParserBase", e);
336         }
337         finally {
338             try {
339                 br.close();
340             }
341             catch (IOException e) {
342                 throw new IllegalArgumentException("unable to close bufferReader in accountingLineParserBase", e);
343             }
344         }
345 
346         return importedAccountingLines;
347     }
348 
349     
350 
351 
352 
353     public final List importSourceAccountingLines(String fileName, InputStream stream, AccountingDocument document) {
354         return importAccountingLines(fileName, stream, document, true);
355     }
356 
357     
358 
359 
360 
361     public final List importTargetAccountingLines(String fileName, InputStream stream, AccountingDocument document) {
362         return importAccountingLines(fileName, stream, document, false);
363     }
364 
365     
366 
367 
368 
369 
370 
371 
372     protected void validateImportedAccountingLine(AccountingLine line, String accountingLineAsString) throws AccountingLineParserException {
373         
374         
375         
376         String overrideCode = line.getOverrideCode();
377         if (!AccountingLineOverride.isValidCode(overrideCode)) {
378             String[] errorParameters = { overrideCode, retrieveAttributeLabel(line.getClass(), OVERRIDE_CODE), accountingLineAsString };
379             throw new AccountingLineParserException("invalid overrride code '" + overrideCode + "' for:" + accountingLineAsString, ERROR_INVALID_PROPERTY_VALUE, errorParameters);
380         }
381     }
382 
383     protected String retrieveAttributeLabel(Class clazz, String attributeName) {
384         String label = SpringContext.getBean(DataDictionaryService.class).getAttributeLabel(clazz, attributeName);
385         if (StringUtils.isBlank(label)) {
386             label = attributeName;
387         }
388         return label;
389     }
390 
391     protected String[] chooseFormat(Class<? extends AccountingLine> accountingLineClass) {
392         String[] format = null;
393         if (SourceAccountingLine.class.isAssignableFrom(accountingLineClass)) {
394             format = getSourceAccountingLineFormat();
395         }
396         else if (TargetAccountingLine.class.isAssignableFrom(accountingLineClass)) {
397             format = getTargetAccountingLineFormat();
398         }
399         else {
400             throw new IllegalStateException("unknow accounting line class: " + accountingLineClass);
401         }
402         return format;
403     }
404 }