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 }