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.batch.dataaccess.impl;
017    
018    import java.util.ArrayList;
019    
020    /**
021     * 
022     * implements a type to hold the raw SQL for each step in a process.  allows us to have an insertion point for system parameters which can only be 
023     * incorporated into the SQL at run-time.  provides a method to insert the parameter string and return the finished SQL as a string,
024     * and another to just return SQL as a string without inserting anything, therefore permitting most of the SQL to be built in a constructor and finished at run time.
025     */
026    
027    public class SQLForStep {
028    
029        // locations in the StringBuilder in which a run-time parameter string is to be incorporated
030        // (maybe an int[] will be faster when a user is waiting for the SQL to be built 
031        private int[] insertionPoints = {};
032        // string buffers are thread-safe, while string builders are not
033        // string builders are more efficient, so use one here and never modify it in our public methods
034        private StringBuilder sqlBuilder;
035        
036        public SQLForStep(StringBuilder sqlBuilder)
037        {
038            // use this constructor when the SQL needs no insertions
039            // make a copy of the static SQL, so this type acts as a container for SQL to be passed ot other methods and used repeatedly.
040            this.sqlBuilder = new StringBuilder(sqlBuilder);
041        }
042        
043        public SQLForStep(StringBuilder sqlBuilder, ArrayList<Integer> insertionPoints)
044        {
045            // use this constructor when there are run-time strings to be inserted into the SQL
046            this.sqlBuilder = new StringBuilder(sqlBuilder);
047            int pointCount = insertionPoints.size();
048            this.insertionPoints = new int[pointCount];
049            for (int idx = 0; idx < pointCount; idx++) 
050            {
051                this.insertionPoints[idx] = (int) insertionPoints.get(idx);
052            }
053        }
054        
055        public String getSQL(ArrayList<String> parameterToInsert)
056        {
057            // make a copy of the SQL, so this routine is thread-safe, and evey caller gets her own copy of the static SQL to change.
058            // string buffers are thread-safe, while string builders are not, but making a copy puts everything on the stack of the local thread anyway,
059            // so we don't need the string buffer synchronization and can use the more efficient StringBuilder.
060            if (parameterToInsert.size() != insertionPoints.length) {
061                // throw an out of bounds exception if there is a mismatch
062                // we can't build the SQL
063                throw(new IndexOutOfBoundsException("the number of strings to be inserted into SQL does not match the number of insertion points"));
064            }
065            StringBuilder unfinishedSQL = new StringBuilder(sqlBuilder);
066            // insertions will add to the length of the String
067            // we need to increase the value of the insertion point based on the leghtns
068            int lengthSoFar = 0;
069            for (int idx = 0; idx < insertionPoints.length; idx++)
070            {
071                String parameterString = parameterToInsert.get(idx);
072                unfinishedSQL.insert(insertionPoints[idx]+lengthSoFar,parameterString);
073                lengthSoFar = lengthSoFar+parameterString.length();
074            }
075            return(unfinishedSQL.toString());
076        }
077        
078        public String getSQL()
079        {
080            // no changes needed to the StringBuilder.  just return a string built from the SQL StringBuilder.
081            return(sqlBuilder.toString());
082        }
083    }