KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > common > sql > filters > MacrosHandler


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Marc Wick.
22  * Contributor(s): ______________________.
23  */

24
25 package org.objectweb.cjdbc.common.sql.filters;
26
27 import java.sql.Date JavaDoc;
28 import java.sql.Time JavaDoc;
29 import java.sql.Timestamp JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Random JavaDoc;
32
33 import org.objectweb.cjdbc.common.util.Strings;
34 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
35 import org.objectweb.cjdbc.common.xml.XmlComponent;
36
37 /**
38  * This class defines a MacrosHandler
39  *
40  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
41  * @version 1.0
42  */

43 public class MacrosHandler implements XmlComponent
44 {
45   /** Used when level is unknown */
46   public static final int UNKNOWN_INT_VALUE = -1;
47   /** Used when level is unknown */
48   public static final String JavaDoc UNKNOWN_STRING_VALUE = "unknown";
49
50   /** String for rand() macro */
51   private static final String JavaDoc MACRO_RAND = "rand()";
52
53   /** Value if rand() macro should not be replaced */
54   public static final int RAND_OFF = 0;
55   /** Value if rand() macro should be replaced by an integer value */
56   public static final int RAND_INT = 1;
57   /** Value if rand() macro should be replaced by an long value */
58   public static final int RAND_LONG = 2;
59   /** Value if rand() macro should be replaced by an float value (default) */
60   public static final int RAND_FLOAT = 3;
61   /** Value if rand() macro should be replaced by an double value */
62   public static final int RAND_DOUBLE = 4;
63
64   private final Random JavaDoc randGenerator = new Random JavaDoc();
65
66   private int replaceRand = RAND_FLOAT;
67
68   /** String for now() macro */
69   private static final String JavaDoc MACRO_NOW = "now()";
70   /** String for current_date macro */
71   private static final String JavaDoc MACRO_CURRENT_DATE = "current_date";
72   /** String for current_times macro */
73   private static final String JavaDoc MACRO_CURRENT_TIME = "current_time";
74   /** String for timeofday() macro */
75   private static final String JavaDoc MACRO_TIMEODFAY = "timeofday()";
76   /** String for current_timestamp macro */
77   private static final String JavaDoc MACRO_CURRENT_TIMESTAMP = "current_timestamp";
78
79   /** Value if a date macro should not be replaced */
80   public static final int DATE_OFF = 0;
81   /** Value if date macro should be replaced by an java.sql.Date value */
82   public static final int DATE_DATE = 1;
83   /** Value if date macro should be replaced by an java.sql.Time value */
84   public static final int DATE_TIME = 2;
85   /** Value if date macro should be replaced by an java.sql.Timestamp value */
86   public static final int DATE_TIMESTAMP = 3;
87
88   private long clockResolution = 0;
89   private int now = DATE_TIMESTAMP;
90   private int currentDate = DATE_DATE;
91   private int currentTime = DATE_TIME;
92   private int timeOfDay = DATE_TIMESTAMP;
93   private int currentTimestamp = DATE_TIMESTAMP;
94
95   private boolean needsProcessing;
96   private boolean needsDateProcessing;
97
98   /**
99    * Creates a new <code>MacrosHandler</code> object
100    *
101    * @param replaceRand replacement of rand() macro
102    * @param clockResolution clock resolution for date macros
103    * @param now replacement of now()
104    * @param currentDate replacement of current_date
105    * @param currentTime replacement of current_time
106    * @param timeOfDay replacement of timeofday()
107    * @param currentTimestamp replacement of current_timestamp
108    */

109   public MacrosHandler(int replaceRand, long clockResolution, int now,
110       int currentDate, int currentTime, int timeOfDay, int currentTimestamp)
111   {
112     if ((replaceRand < RAND_OFF) || (replaceRand > RAND_DOUBLE))
113       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_RAND
114           + " macro replacement (" + replaceRand + ")");
115     this.replaceRand = replaceRand;
116     if (clockResolution < 0)
117       throw new RuntimeException JavaDoc(
118           "Invalid negative value for clock resolution in date macros");
119     this.clockResolution = clockResolution;
120     if ((now < DATE_OFF) || (now > DATE_TIMESTAMP))
121       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_NOW
122           + " macro replacement (" + now + ")");
123     this.now = now;
124     if ((currentDate < DATE_OFF) || (currentDate > DATE_DATE))
125       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_CURRENT_DATE
126           + " macro replacement (" + currentDate + ")");
127     this.currentDate = currentDate;
128     if ((currentTime < DATE_OFF) || (currentTime > DATE_TIMESTAMP))
129       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_CURRENT_TIME
130           + " macro replacement (" + currentTime + ")");
131     this.currentTime = currentTime;
132     if ((timeOfDay < DATE_OFF) || (timeOfDay > DATE_TIMESTAMP))
133       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_TIMEODFAY
134           + " macro replacement (" + timeOfDay + ")");
135     this.timeOfDay = timeOfDay;
136     if ((currentTimestamp < DATE_OFF) || (currentTimestamp > DATE_TIMESTAMP))
137       throw new RuntimeException JavaDoc("Invalid value for " + MACRO_CURRENT_TIMESTAMP
138           + " macro replacement (" + currentTimestamp + ")");
139     this.currentTimestamp = currentTimestamp;
140     needsDateProcessing = (now + currentDate + timeOfDay + currentTimestamp) > 0;
141     needsProcessing = needsDateProcessing || (replaceRand > 0);
142   }
143
144   /**
145    * Convert the rand level from string (xml value) to integer
146    *
147    * @param randLevel the rand level
148    * @return an int corresponding to the string description
149    */

150   public static final int getIntRandLevel(String JavaDoc randLevel)
151   {
152     if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_off))
153       return RAND_OFF;
154     else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_double))
155       return RAND_DOUBLE;
156     else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_float))
157       return RAND_FLOAT;
158     else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_int))
159       return RAND_INT;
160     else if (randLevel.equalsIgnoreCase(DatabasesXmlTags.VAL_long))
161       return RAND_LONG;
162     else
163       return UNKNOWN_INT_VALUE;
164   }
165
166   /**
167    * Return this <code>MacrosHandler</code> to the corresponding xml form
168    *
169    * @return the XML representation of this element
170    */

171   public String JavaDoc getXml()
172   {
173     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
174     sb.append("<" + DatabasesXmlTags.ELT_MacroHandling + " "
175         + DatabasesXmlTags.ATT_rand + "=\"" + getStringRandLevel(replaceRand)
176         + "\" " + DatabasesXmlTags.ATT_now + "=\"" + getStringDateLevel(now)
177         + "\" " + DatabasesXmlTags.ATT_currentDate + "=\""
178         + getStringDateLevel(currentDate) + "\" "
179         + DatabasesXmlTags.ATT_currentTime + "=\""
180         + getStringDateLevel(currentTime) + "\" "
181         + DatabasesXmlTags.ATT_currentTimestamp + "=\""
182         + getStringDateLevel(currentTimestamp) + "\" "
183         + DatabasesXmlTags.ATT_timeOfDay + "=\""
184         + getStringDateLevel(timeOfDay) + "\" "
185         + DatabasesXmlTags.ATT_timeResolution + "=\"" + clockResolution + "\" "
186         + "/>");
187     return sb.toString();
188   }
189
190   /**
191    * Convert the rand level from int (java code) to string (xml value)
192    *
193    * @param randLevel the rand level
194    * @return a string description corresponding to that level
195    */

196   public static final String JavaDoc getStringRandLevel(int randLevel)
197   {
198     switch (randLevel)
199     {
200       case RAND_OFF :
201         return DatabasesXmlTags.VAL_off;
202       case RAND_DOUBLE :
203         return DatabasesXmlTags.VAL_double;
204       case RAND_FLOAT :
205         return DatabasesXmlTags.VAL_float;
206       case RAND_INT :
207         return DatabasesXmlTags.VAL_int;
208       case RAND_LONG :
209         return DatabasesXmlTags.VAL_long;
210       default :
211         return UNKNOWN_STRING_VALUE;
212     }
213   }
214
215   /**
216    * Convert the date level from string (xml value) to integer
217    *
218    * @param dateLevel the date level
219    * @return an int corresponding to the string description
220    */

221   public static final int getIntDateLevel(String JavaDoc dateLevel)
222   {
223     if (dateLevel.equals(DatabasesXmlTags.VAL_off))
224       return DATE_OFF;
225     else if (dateLevel.equals(DatabasesXmlTags.VAL_date))
226       return DATE_DATE;
227     else if (dateLevel.equals(DatabasesXmlTags.VAL_time))
228       return DATE_TIME;
229     else if (dateLevel.equals(DatabasesXmlTags.VAL_timestamp))
230       return DATE_TIMESTAMP;
231     else
232       return UNKNOWN_INT_VALUE;
233   }
234
235   /**
236    * Convert the date level from int (java code) to string (xml value)
237    *
238    * @param dateLevel the date level
239    * @return a string description corresponding to that level
240    */

241   public static final String JavaDoc getStringDateLevel(int dateLevel)
242   {
243     switch (dateLevel)
244     {
245       case DATE_OFF :
246         return DatabasesXmlTags.VAL_off;
247       case DATE_DATE :
248         return DatabasesXmlTags.VAL_date;
249       case DATE_TIME :
250         return DatabasesXmlTags.VAL_time;
251       case DATE_TIMESTAMP :
252         return DatabasesXmlTags.VAL_timestamp;
253       default :
254         return UNKNOWN_STRING_VALUE;
255     }
256   }
257
258   /**
259    * Processes a date related macro using the given timestamp.
260    *
261    * @param originalSql original SQL request
262    * @param macroPattern macro text to look for
263    * @param replacementPolicy DATE_DATE, DATE_TIME or DATE_TIMESTAMP
264    * @param currentClock current time in ms
265    * @param idxs quote indexes
266    * @return new SQL statement
267    */

268   public String JavaDoc macroDate(String JavaDoc originalSql, String JavaDoc macroPattern,
269       int replacementPolicy, long currentClock, Integer JavaDoc[] idxs)
270   {
271     if (idxs == null)
272       idxs = getQuoteIndexes(originalSql);
273     String JavaDoc lower = originalSql.toLowerCase();
274     int idx = lower.indexOf(macroPattern.toLowerCase());
275     if (idx == -1 || !shouldReplaceMacro(idx, idxs))
276       return originalSql;
277
278     String JavaDoc date;
279     switch (replacementPolicy)
280     {
281       case DATE_DATE :
282         date = "{d '" + new Date JavaDoc(currentClock).toString() + "'}";
283         break;
284       case DATE_TIME :
285         date = "{t '" + new Time JavaDoc(currentClock).toString() + "'}";
286         break;
287       case DATE_TIMESTAMP :
288         date = "{ts '" + new Timestamp JavaDoc(currentClock).toString() + "'}";
289         break;
290       default :
291         throw new RuntimeException JavaDoc(
292             "Unexpected replacement strategy for date macro ("
293                 + replacementPolicy + ")");
294     }
295     return Strings.replaceCasePreserving(originalSql, macroPattern, date);
296   }
297
298   /**
299    * Replaces rand() with a randomized value.
300    *
301    * @param originalSql original SQL request
302    * @param idxs quote indexes
303    * @return new SQL statement
304    */

305   public String JavaDoc macroRand(String JavaDoc originalSql, Integer JavaDoc[] idxs)
306   {
307     if (idxs == null)
308       idxs = getQuoteIndexes(originalSql);
309     String JavaDoc lower = originalSql.toLowerCase();
310     int idx = lower.indexOf(MACRO_RAND);
311     if (idx > 0)
312     {
313       String JavaDoc rand;
314       StringBuffer JavaDoc sql = new StringBuffer JavaDoc(originalSql);
315       int shift = 0;
316       do
317       {
318         if (shouldReplaceMacro(idx, idxs))
319         {
320           switch (replaceRand)
321           {
322             case RAND_INT :
323               rand = Integer.toString(randGenerator.nextInt());
324               break;
325             case RAND_LONG :
326               rand = Long.toString(randGenerator.nextLong());
327               break;
328             case RAND_FLOAT :
329               rand = Float.toString(randGenerator.nextFloat());
330               break;
331             case RAND_DOUBLE :
332               rand = Double.toString(randGenerator.nextDouble());
333               break;
334             default :
335               throw new RuntimeException JavaDoc(
336                   "Unexpected replacement strategy for rand() macro ("
337                       + replaceRand + ")");
338           }
339           sql = sql.replace(idx + shift, idx + shift + MACRO_RAND.length(),
340               rand);
341           shift += rand.length() - MACRO_RAND.length();
342         }
343         idx = lower.indexOf(MACRO_RAND, idx + MACRO_RAND.length());
344       }
345       while (idx > 0);
346       return sql.toString();
347     }
348     else
349     {
350       return originalSql;
351     }
352   }
353
354   /**
355    * Processes all macros in the given request and returns a new String with the
356    * processed macros. If no macro has to be processed, the original String is
357    * returned.
358    *
359    * @param sql SQL statement to process
360    * @return processed statement
361    */

362   public final String JavaDoc processMacros(String JavaDoc sql)
363   {
364     if (!needsProcessing)
365       return sql;
366     if(sql==null)
367       return null;
368     Integer JavaDoc[] idxs = this.getQuoteIndexes(sql);
369     if (replaceRand > RAND_OFF)
370       sql = macroRand(sql, idxs);
371     if (!needsDateProcessing)
372       return sql;
373     long currentClock = System.currentTimeMillis();
374     if (clockResolution > 0)
375       currentClock = currentClock - (currentClock % clockResolution);
376     if (now > DATE_OFF)
377       sql = macroDate(sql, MACRO_NOW, now, currentClock, idxs);
378     if (currentDate > DATE_OFF)
379       sql = macroDate(sql, MACRO_CURRENT_DATE, currentDate, currentClock, idxs);
380     if (currentTimestamp > DATE_OFF)
381       sql = macroDate(sql, MACRO_CURRENT_TIMESTAMP, currentTimestamp,
382           currentClock, idxs);
383     if (currentTime > DATE_OFF)
384       sql = macroDate(sql, MACRO_CURRENT_TIME, currentTime, currentClock, idxs);
385     if (timeOfDay > DATE_OFF)
386       sql = macroDate(sql, MACRO_TIMEODFAY, timeOfDay, currentClock, idxs);
387
388     return sql;
389   }
390
391   /**
392    * Retrieve all the indexes of quotes in the string
393    *
394    * @param sql the original query
395    * @return an array of integer corresponding to the quote indexes
396    */

397   private Integer JavaDoc[] getQuoteIndexes(String JavaDoc sql)
398   {
399     ArrayList JavaDoc list = new ArrayList JavaDoc();
400     for (int i = 0; i < sql.length(); i++)
401     {
402       char c = sql.charAt(i);
403       if (c == '\'')
404         list.add(new Integer JavaDoc(i));
405     }
406     Integer JavaDoc[] intlist = new Integer JavaDoc[list.size()];
407     return (Integer JavaDoc[]) list.toArray(intlist);
408   }
409
410   /**
411    * Should we replace a macro situated at index idx, knowing that the quotes
412    * are at indexes list
413    *
414    * @param idx the index of the macro
415    * @param list the indexes of quotes
416    * @return <code>true</code> if we should change the macro,
417    * <code>false</code> if the macro is within a string
418    */

419   private boolean shouldReplaceMacro(int idx, Integer JavaDoc[] list)
420   {
421     int count = 0;
422     while (count < list.length && list[count].intValue() < idx)
423     {
424       count++;
425     }
426     return count % 2 == 0;
427   }
428
429 }
Popular Tags