00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package org.objectweb.cjdbc.common.sql.filters;
00026
00027 import java.sql.Date;
00028 import java.sql.Time;
00029 import java.sql.Timestamp;
00030 import java.util.ArrayList;
00031 import java.util.Random;
00032
00033 import org.objectweb.cjdbc.common.util.Strings;
00034 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00035 import org.objectweb.cjdbc.common.xml.XmlComponent;
00036
00037
00038
00039
00040
00041
00042
00043 public class MacrosHandler implements XmlComponent
00044 {
00045
00046 public static final int UNKNOWN_INT_VALUE = -1;
00047
00048 public static final String UNKNOWN_STRING_VALUE = "unknown";
00049
00050
00051 private static final String MACRO_RAND = "rand()";
00052
00053
00054 public static final int RAND_OFF = 0;
00055
00056 public static final int RAND_INT = 1;
00057
00058 public static final int RAND_LONG = 2;
00059
00060 public static final int RAND_FLOAT = 3;
00061
00062 public static final int RAND_DOUBLE = 4;
00063
00064 private final Random randGenerator = new Random();
00065
00066 private int replaceRand = RAND_FLOAT;
00067
00068
00069 private static final String MACRO_NOW = "now()";
00070
00071 private static final String MACRO_CURRENT_DATE = "current_date";
00072
00073 private static final String MACRO_CURRENT_TIME = "current_time";
00074
00075 private static final String MACRO_TIMEODFAY = "timeofday()";
00076
00077 private static final String MACRO_CURRENT_TIMESTAMP = "current_timestamp";
00078
00079
00080 public static final int DATE_OFF = 0;
00081
00082 public static final int DATE_DATE = 1;
00083
00084 public static final int DATE_TIME = 2;
00085
00086 public static final int DATE_TIMESTAMP = 3;
00087
00088 private long clockResolution = 0;
00089 private int now = DATE_TIMESTAMP;
00090 private int currentDate = DATE_DATE;
00091 private int currentTime = DATE_TIME;
00092 private int timeOfDay = DATE_TIMESTAMP;
00093 private int currentTimestamp = DATE_TIMESTAMP;
00094
00095 private boolean needsProcessing;
00096 private boolean needsDateProcessing;
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 public MacrosHandler(int replaceRand, long clockResolution, int now,
00110 int currentDate, int currentTime, int timeOfDay, int currentTimestamp)
00111 {
00112 if ((replaceRand < RAND_OFF) || (replaceRand > RAND_DOUBLE))
00113 throw new RuntimeException("Invalid value for " + MACRO_RAND
00114 + " macro replacement (" + replaceRand + ")");
00115 this.replaceRand = replaceRand;
00116 if (clockResolution < 0)
00117 throw new RuntimeException(
00118 "Invalid negative value for clock resolution in date macros");
00119 this.clockResolution = clockResolution;
00120 if ((now < DATE_OFF) || (now > DATE_TIMESTAMP))
00121 throw new RuntimeException("Invalid value for " + MACRO_NOW
00122 + " macro replacement (" + now + ")");
00123 this.now = now;
00124 if ((currentDate < DATE_OFF) || (currentDate > DATE_DATE))
00125 throw new RuntimeException("Invalid value for " + MACRO_CURRENT_DATE
00126 + " macro replacement (" + currentDate + ")");
00127 this.currentDate = currentDate;
00128 if ((currentTime < DATE_OFF) || (currentTime > DATE_TIMESTAMP))
00129 throw new RuntimeException("Invalid value for " + MACRO_CURRENT_TIME
00130 + " macro replacement (" + currentTime + ")");
00131 this.currentTime = currentTime;
00132 if ((timeOfDay < DATE_OFF) || (timeOfDay > DATE_TIMESTAMP))
00133 throw new RuntimeException("Invalid value for " + MACRO_TIMEODFAY
00134 + " macro replacement (" + timeOfDay + ")");
00135 this.timeOfDay = timeOfDay;
00136 if ((currentTimestamp < DATE_OFF) || (currentTimestamp > DATE_TIMESTAMP))
00137 throw new RuntimeException("Invalid value for " + MACRO_CURRENT_TIMESTAMP
00138 + " macro replacement (" + currentTimestamp + ")");
00139 this.currentTimestamp = currentTimestamp;
00140 needsDateProcessing = (now + currentDate + timeOfDay + currentTimestamp) > 0;
00141 needsProcessing = needsDateProcessing || (replaceRand > 0);
00142 }
00143
00144
00145
00146
00147
00148
00149
00150 public static final int getIntRandLevel(String randLevel)
00151 {
00152 if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_off))
00153 return RAND_OFF;
00154 else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_double))
00155 return RAND_DOUBLE;
00156 else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_float))
00157 return RAND_FLOAT;
00158 else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_int))
00159 return RAND_INT;
00160 else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_long))
00161 return RAND_LONG;
00162 else
00163 return UNKNOWN_INT_VALUE;
00164 }
00165
00166
00167
00168
00169
00170
00171 public String getXml()
00172 {
00173 StringBuffer sb = new StringBuffer();
00174 sb.append("<" + DatabasesXmlTags.ELT_MacroHandling + " "
00175 + DatabasesXmlTags.ATT_rand + "=\"" + getStringRandLevel(replaceRand)
00176 + "\" " + DatabasesXmlTags.ATT_now + "=\"" + getStringDateLevel(now)
00177 + "\" " + DatabasesXmlTags.ATT_currentDate + "=\""
00178 + getStringDateLevel(currentDate) + "\" "
00179 + DatabasesXmlTags.ATT_currentTime + "=\""
00180 + getStringDateLevel(currentTime) + "\" "
00181 + DatabasesXmlTags.ATT_currentTimestamp + "=\""
00182 + getStringDateLevel(currentTimestamp) + "\" "
00183 + DatabasesXmlTags.ATT_timeOfDay + "=\""
00184 + getStringDateLevel(timeOfDay) + "\" "
00185 + DatabasesXmlTags.ATT_timeResolution + "=\"" + clockResolution + "\" "
00186 + "/>");
00187 return sb.toString();
00188 }
00189
00190
00191
00192
00193
00194
00195
00196 public static final String getStringRandLevel(int randLevel)
00197 {
00198 switch (randLevel)
00199 {
00200 case RAND_OFF :
00201 return DatabasesXmlTags.VAL_off;
00202 case RAND_DOUBLE :
00203 return DatabasesXmlTags.VAL_double;
00204 case RAND_FLOAT :
00205 return DatabasesXmlTags.VAL_float;
00206 case RAND_INT :
00207 return DatabasesXmlTags.VAL_int;
00208 case RAND_LONG :
00209 return DatabasesXmlTags.VAL_long;
00210 default :
00211 return UNKNOWN_STRING_VALUE;
00212 }
00213 }
00214
00215
00216
00217
00218
00219
00220
00221 public static final int getIntDateLevel(String dateLevel)
00222 {
00223 if (dateLevel.equals(DatabasesXmlTags.VAL_off))
00224 return DATE_OFF;
00225 else if (dateLevel.equals(DatabasesXmlTags.VAL_date))
00226 return DATE_DATE;
00227 else if (dateLevel.equals(DatabasesXmlTags.VAL_time))
00228 return DATE_TIME;
00229 else if (dateLevel.equals(DatabasesXmlTags.VAL_timestamp))
00230 return DATE_TIMESTAMP;
00231 else
00232 return UNKNOWN_INT_VALUE;
00233 }
00234
00235
00236
00237
00238
00239
00240
00241 public static final String getStringDateLevel(int dateLevel)
00242 {
00243 switch (dateLevel)
00244 {
00245 case DATE_OFF :
00246 return DatabasesXmlTags.VAL_off;
00247 case DATE_DATE :
00248 return DatabasesXmlTags.VAL_date;
00249 case DATE_TIME :
00250 return DatabasesXmlTags.VAL_time;
00251 case DATE_TIMESTAMP :
00252 return DatabasesXmlTags.VAL_timestamp;
00253 default :
00254 return UNKNOWN_STRING_VALUE;
00255 }
00256 }
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 public String macroDate(String originalSql, String macroPattern,
00269 int replacementPolicy, long currentClock, Integer[] idxs)
00270 {
00271 if (idxs == null)
00272 idxs = getQuoteIndexes(originalSql);
00273 String lower = originalSql.toLowerCase();
00274 int idx = lower.indexOf(macroPattern.toLowerCase());
00275 if (idx == -1 || !shouldReplaceMacro(idx, idxs))
00276 return originalSql;
00277
00278 String date;
00279 switch (replacementPolicy)
00280 {
00281 case DATE_DATE :
00282 date = "{d '" + new Date(currentClock).toString() + "'}";
00283 break;
00284 case DATE_TIME :
00285 date = "{t '" + new Time(currentClock).toString() + "'}";
00286 break;
00287 case DATE_TIMESTAMP :
00288 date = "{ts '" + new Timestamp(currentClock).toString() + "'}";
00289 break;
00290 default :
00291 throw new RuntimeException(
00292 "Unexpected replacement strategy for date macro ("
00293 + replacementPolicy + ")");
00294 }
00295 return Strings.replaceCasePreserving(originalSql, macroPattern, date);
00296 }
00297
00298
00299
00300
00301
00302
00303
00304
00305 public String macroRand(String originalSql, Integer[] idxs)
00306 {
00307 if (idxs == null)
00308 idxs = getQuoteIndexes(originalSql);
00309 String lower = originalSql.toLowerCase();
00310 int idx = lower.indexOf(MACRO_RAND);
00311 if (idx > 0)
00312 {
00313 String rand;
00314 StringBuffer sql = new StringBuffer(originalSql);
00315 int shift = 0;
00316 do
00317 {
00318 if (shouldReplaceMacro(idx, idxs))
00319 {
00320 switch (replaceRand)
00321 {
00322 case RAND_INT :
00323 rand = Integer.toString(randGenerator.nextInt());
00324 break;
00325 case RAND_LONG :
00326 rand = Long.toString(randGenerator.nextLong());
00327 break;
00328 case RAND_FLOAT :
00329 rand = Float.toString(randGenerator.nextFloat());
00330 break;
00331 case RAND_DOUBLE :
00332 rand = Double.toString(randGenerator.nextDouble());
00333 break;
00334 default :
00335 throw new RuntimeException(
00336 "Unexpected replacement strategy for rand() macro ("
00337 + replaceRand + ")");
00338 }
00339 sql = sql.replace(idx + shift, idx + shift + MACRO_RAND.length(),
00340 rand);
00341 shift += rand.length() - MACRO_RAND.length();
00342 }
00343 idx = lower.indexOf(MACRO_RAND, idx + MACRO_RAND.length());
00344 }
00345 while (idx > 0);
00346 return sql.toString();
00347 }
00348 else
00349 {
00350 return originalSql;
00351 }
00352 }
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362 public final String processMacros(String sql)
00363 {
00364 if (!needsProcessing)
00365 return sql;
00366 if(sql==null)
00367 return null;
00368 Integer[] idxs = this.getQuoteIndexes(sql);
00369 if (replaceRand > RAND_OFF)
00370 sql = macroRand(sql, idxs);
00371 if (!needsDateProcessing)
00372 return sql;
00373 long currentClock = System.currentTimeMillis();
00374 if (clockResolution > 0)
00375 currentClock = currentClock - (currentClock % clockResolution);
00376 if (now > DATE_OFF)
00377 sql = macroDate(sql, MACRO_NOW, now, currentClock, idxs);
00378 if (currentDate > DATE_OFF)
00379 sql = macroDate(sql, MACRO_CURRENT_DATE, currentDate, currentClock, idxs);
00380 if (currentTimestamp > DATE_OFF)
00381 sql = macroDate(sql, MACRO_CURRENT_TIMESTAMP, currentTimestamp,
00382 currentClock, idxs);
00383 if (currentTime > DATE_OFF)
00384 sql = macroDate(sql, MACRO_CURRENT_TIME, currentTime, currentClock, idxs);
00385 if (timeOfDay > DATE_OFF)
00386 sql = macroDate(sql, MACRO_TIMEODFAY, timeOfDay, currentClock, idxs);
00387
00388 return sql;
00389 }
00390
00391
00392
00393
00394
00395
00396
00397 private Integer[] getQuoteIndexes(String sql)
00398 {
00399 ArrayList list = new ArrayList();
00400 for (int i = 0; i < sql.length(); i++)
00401 {
00402 char c = sql.charAt(i);
00403 if (c == '\'')
00404 list.add(new Integer(i));
00405 }
00406 Integer[] intlist = new Integer[list.size()];
00407 return (Integer[]) list.toArray(intlist);
00408 }
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419 private boolean shouldReplaceMacro(int idx, Integer[] list)
00420 {
00421 int count = 0;
00422 while (count < list.length && list[count].intValue() < idx)
00423 {
00424 count++;
00425 }
00426 return count % 2 == 0;
00427 }
00428
00429 }