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 }