001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.bc.util;
017    
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.List;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.kuali.kfs.module.bc.BCConstants;
024    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionRequestMove;
025    import org.kuali.rice.kns.util.KualiInteger;
026    
027    /**
028     * This class contains methods to help parse budget construction import request files
029     * 
030     */
031    public class ImportRequestFileParsingHelper {
032    
033        
034        /**
035         * Parses line and creates BudgetConstructionRequestMove object. 
036         * 
037         * @param lineToParse
038         * @param fieldSeperator
039         * @param textDelimiter
040         * @return the BudgetConstructionRequestMove or null if there was an error parsing the line
041         */
042        public static BudgetConstructionRequestMove parseLine(String lineToParse, String fieldSeperator, String textDelimiter, boolean isAnnual) {
043            List<String> attributes = new ArrayList<String>();
044            BudgetConstructionRequestMove budgetConstructionRequestMove = new BudgetConstructionRequestMove();
045            
046            int expectedNumberOfSeparators = isAnnual ? 5 : 16;
047            
048            lineToParse = lineToParse.trim();
049            
050            //check if line is in correct format
051            if (!isLineCorrectlyFormatted(lineToParse, fieldSeperator, textDelimiter, isAnnual)) return null;
052            
053            if (textDelimiter.equalsIgnoreCase(BCConstants.RequestImportTextFieldDelimiter.NOTHING.getDelimiter())) {
054                attributes.addAll(Arrays.asList(lineToParse.split(isFieldSeparatorSpecialCharacter(fieldSeperator) ? "\\" + fieldSeperator : fieldSeperator)));
055            } else if ( getEscapedFieldSeparatorCount(lineToParse, fieldSeperator, textDelimiter, isAnnual) == 0) {
056                lineToParse = StringUtils.remove(lineToParse, textDelimiter);
057                attributes.addAll(Arrays.asList(lineToParse.split(isFieldSeparatorSpecialCharacter(fieldSeperator) ? "\\" + fieldSeperator : fieldSeperator)));
058            } else {
059                int firstIndexOfTextDelimiter = 0;
060                int nextIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, firstIndexOfTextDelimiter + 1);
061                int expectedNumberOfTextDelimiters = 10;
062                
063                for (int i = 0; i < expectedNumberOfTextDelimiters/2; i++) {
064                    attributes.add(lineToParse.substring(firstIndexOfTextDelimiter, nextIndexOfTextDelimiter).replaceAll(textDelimiter, ""));
065                    firstIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, nextIndexOfTextDelimiter + 1);
066                    nextIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, firstIndexOfTextDelimiter + 1);
067                }
068                
069                String remainingNonStringValuesToParse = lineToParse.substring(lineToParse.lastIndexOf(textDelimiter + 1));
070                attributes.addAll(Arrays.asList(remainingNonStringValuesToParse.split(isFieldSeparatorSpecialCharacter(fieldSeperator) ? "\\" + fieldSeperator : fieldSeperator)));
071            }
072            
073            budgetConstructionRequestMove.setChartOfAccountsCode(attributes.get(0));
074            budgetConstructionRequestMove.setAccountNumber(attributes.get(1));
075            budgetConstructionRequestMove.setSubAccountNumber(attributes.get(2));
076            budgetConstructionRequestMove.setFinancialObjectCode(attributes.get(3));
077            budgetConstructionRequestMove.setFinancialSubObjectCode(attributes.get(4));
078            
079            try {
080                if (isAnnual) {
081                    budgetConstructionRequestMove.setAccountLineAnnualBalanceAmount(new KualiInteger(Integer.parseInt(attributes.get(5))));
082                    
083                } else {
084                    budgetConstructionRequestMove.setFinancialDocumentMonth1LineAmount(new KualiInteger(Integer.parseInt(attributes.get(5))));
085                    budgetConstructionRequestMove.setFinancialDocumentMonth2LineAmount(new KualiInteger(Integer.parseInt(attributes.get(6))));
086                    budgetConstructionRequestMove.setFinancialDocumentMonth3LineAmount(new KualiInteger(Integer.parseInt(attributes.get(7))));
087                    budgetConstructionRequestMove.setFinancialDocumentMonth4LineAmount(new KualiInteger(Integer.parseInt(attributes.get(8))));
088                    budgetConstructionRequestMove.setFinancialDocumentMonth5LineAmount(new KualiInteger(Integer.parseInt(attributes.get(9))));
089                    budgetConstructionRequestMove.setFinancialDocumentMonth6LineAmount(new KualiInteger(Integer.parseInt(attributes.get(10))));
090                    budgetConstructionRequestMove.setFinancialDocumentMonth7LineAmount(new KualiInteger(Integer.parseInt(attributes.get(11))));
091                    budgetConstructionRequestMove.setFinancialDocumentMonth8LineAmount(new KualiInteger(Integer.parseInt(attributes.get(12))));
092                    budgetConstructionRequestMove.setFinancialDocumentMonth9LineAmount(new KualiInteger(Integer.parseInt(attributes.get(13))));
093                    budgetConstructionRequestMove.setFinancialDocumentMonth10LineAmount(new KualiInteger(Integer.parseInt(attributes.get(14))));
094                    budgetConstructionRequestMove.setFinancialDocumentMonth11LineAmount(new KualiInteger(Integer.parseInt(attributes.get(15))));
095                    budgetConstructionRequestMove.setFinancialDocumentMonth12LineAmount(new KualiInteger(Integer.parseInt(attributes.get(16))));
096                    
097                }
098            }
099            catch (NumberFormatException e) {
100                return null;
101            }
102    
103            return budgetConstructionRequestMove;
104        }
105        
106        /**
107         * Checks for the correct number of field separators and text delimiters on line (either annual or monthly file). Does not check if the correct field separator and text delimiter are being used (form level validation should do this)
108         * 
109         * @param lineToParse
110         * @param fieldSeperator
111         * @param textDelimiter
112         * @param isAnnual
113         * @return
114         */
115        public static boolean isLineCorrectlyFormatted(String lineToParse, String fieldSeperator, String textDelimiter, boolean isAnnual) {
116            int fieldSeparatorCount = StringUtils.countMatches(lineToParse, fieldSeperator);
117            int expectedNumberOfSeparators = isAnnual ? 5 : 16;
118            
119            if (textDelimiter.equalsIgnoreCase(BCConstants.RequestImportTextFieldDelimiter.NOTHING.getDelimiter())) {
120                
121                if (isAnnual) {
122                    if (fieldSeparatorCount != 5) {
123                        return false;
124                    } 
125                } else {
126                    if (fieldSeparatorCount != 16) {
127                        return false;
128                    } 
129                }
130            } else if (StringUtils.countMatches(lineToParse, textDelimiter) != 10) {
131                return false;
132            } else if ( getEscapedFieldSeparatorCount(lineToParse, fieldSeperator, textDelimiter, isAnnual) == -1 || ( fieldSeparatorCount - getEscapedFieldSeparatorCount(lineToParse, fieldSeperator, textDelimiter, isAnnual) != expectedNumberOfSeparators ) ) {
133                return false;
134            }
135            
136            return true;
137        }
138        
139        /**
140         * Checks if a line of an annual file contains an escaped field separator (convience method to aid in file parsing)
141         * Will not work correctly if text delimiters are not correctly placed in lineToParse (method does not check file formatting)
142         * 
143         * @param lineToParse
144         * @param fieldSeperator
145         * @param textDelimiter
146         * @return number of escaped separators or -1 if file is incorrectly formatted
147         */
148        private static int getEscapedFieldSeparatorCount(String lineToParse, String fieldSeperator, String textDelimiter, boolean isAnnual) {
149            int firstIndexOfTextDelimiter = 0;
150            int nextIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, firstIndexOfTextDelimiter + 1);
151            int expectedNumberOfSeparators = isAnnual ? 5 : 16;
152            int expectedTextDelimitersCount = 10;
153            int actualNumberOfTextDelimiters = StringUtils.countMatches(lineToParse, textDelimiter);
154            int totalSeparatorsInLineToParse = StringUtils.countMatches(lineToParse, fieldSeperator);
155            int escapedSeparatorsCount = 0;
156            
157            //line does not use text delimiters
158            if (textDelimiter.equalsIgnoreCase(BCConstants.RequestImportTextFieldDelimiter.NOTHING.getDelimiter())) return 0;
159            
160            //line does not contain escaped field separators
161            if (totalSeparatorsInLineToParse == expectedNumberOfSeparators) return 0;
162            
163            //line is incorrectly formatted
164            if ( actualNumberOfTextDelimiters != expectedTextDelimitersCount) return -1;
165            
166            for (int i = 0; i < expectedTextDelimitersCount/2; i++) {
167                String escapedString = lineToParse.substring(firstIndexOfTextDelimiter, nextIndexOfTextDelimiter);
168                escapedSeparatorsCount += StringUtils.countMatches(escapedString, fieldSeperator);
169                firstIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, nextIndexOfTextDelimiter + 1);
170                nextIndexOfTextDelimiter = lineToParse.indexOf(textDelimiter, firstIndexOfTextDelimiter + 1);
171            }
172            
173            return escapedSeparatorsCount;
174        }    
175        
176        /**
177         * Determines if the field delimiter is a regular expression special character
178         * 
179         * @param delimiter
180         * @return true if special character
181         */
182        private static boolean isFieldSeparatorSpecialCharacter(String delimiter) {
183            if (delimiter.equals(".")) return true;
184            if (delimiter.equals("[")) return true;
185            if (delimiter.equals("\\")) return true;
186            if (delimiter.equals("*")) return true;
187            if (delimiter.equals("^")) return true;
188            if (delimiter.equals("$")) return true;
189            
190            return false;
191        }
192        
193    //    private static ImportRequestLine createImportRequestLine(boolean isAnnual, int lineNumber) {
194    //        ImportRequestLine line = isAnnual ? new ImportRequestAnnualLine(lineNumber) : new ImportRequestMonthlyLine(lineNumber);
195    //        
196    //        return line;
197    //    }
198    }