KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > sql > macros > MacrosHandler


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: sequoia@continuent.org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * Initial developer(s): Marc Wick.
20  * Contributor(s): ______________________.
21  */

22
23 package org.continuent.sequoia.controller.sql.macros;
24
25 import java.util.ArrayList JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.regex.Matcher JavaDoc;
29
30 import org.continuent.sequoia.common.log.Trace;
31 import org.continuent.sequoia.common.xml.DatabasesXmlTags;
32 import org.continuent.sequoia.common.xml.XmlComponent;
33 import org.continuent.sequoia.controller.requests.AbstractRequest;
34
35 /**
36  * This class defines a MacrosHandler
37  *
38  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
39  * @version 1.0
40  */

41 public class MacrosHandler implements XmlComponent
42 {
43   private List JavaDoc macros;
44   private Trace logger = Trace
45                            .getLogger("org.continuent.sequoia.controller.RequestManager");
46
47   /**
48    * Creates a new <code>MacrosHandler</code> object
49    */

50   public MacrosHandler()
51   {
52     macros = new ArrayList JavaDoc();
53   }
54
55   /**
56    * Add a macro definition to process. If a macro with the same name exists, it
57    * is replaced with exactly the same rank.
58    *
59    * @param macro the new macro
60    */

61   public synchronized void addMacro(AbstractMacro macro)
62   {
63     if (macros.contains(macro))
64     {
65       if (logger.isInfoEnabled())
66         logger.info("Replacing macro " + macro.getMacroName());
67       macros.set(macros.indexOf(macro), macro);
68     }
69     else
70     {
71       if (logger.isDebugEnabled())
72         logger.debug("Adding macro processing for macro "
73             + macro.getMacroName());
74       macros.add(macro);
75     }
76   }
77
78   /**
79    * Remove the given macro
80    *
81    * @param macro the macro to remove
82    * @return true if the macro was successfully removed
83    */

84   public synchronized boolean removeMacro(AbstractMacro macro)
85   {
86     return macros.remove(macro);
87   }
88
89   /**
90    * Return this <code>MacrosHandler</code> to the corresponding xml form
91    *
92    * @return the XML representation of this element
93    */

94   public String JavaDoc getXml()
95   {
96     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
97     sb.append("<" + DatabasesXmlTags.ELT_MacroHandling + ">");
98     for (Iterator JavaDoc iter = macros.iterator(); iter.hasNext();)
99     {
100       AbstractMacro macro = (AbstractMacro) iter.next();
101       sb.append(macro.getXml());
102     }
103     sb.append("</" + DatabasesXmlTags.ELT_MacroHandling + ">");
104     return sb.toString();
105   }
106
107   /**
108    * Processes all macros in the given request and returns a new String with the
109    * processed macros. If no macro has to be processed, the original String is
110    * returned.
111    *
112    * @param request the request to process
113    */

114   public final void processMacros(AbstractRequest request)
115   {
116     if (macros.isEmpty() || request.getMacrosAreProcessed())
117       return;
118
119     if (logger.isDebugEnabled())
120       logger.debug("Processing macros in request " + request.getId() + " ("
121           + request + ")");
122
123     // Replace the statement or the template
124
request.setSqlOrTemplate(replaceMacrosInString(request.getSqlOrTemplate()));
125
126     // Process the parameters if any
127
if (request.getPreparedStatementParameters() != null)
128       request.setPreparedStatementParameters(replaceMacrosInString(request
129           .getPreparedStatementParameters()));
130
131     if (logger.isDebugEnabled())
132       logger.debug("Macros processed in request " + request.getId()
133           + ", new request is: " + request);
134   }
135
136   /**
137    * Processes a macro using the given timestamp.
138    *
139    * @param quotes the query to replace with the position of its quotes
140    * @param macro the macro to apply
141    * @param currentClock current time in ms
142    * @return new SQL statement
143    */

144   private QuoteIndicesAndReplacedSql processMacro(
145       QuoteIndicesAndReplacedSql quotes, AbstractMacro macro, long currentClock)
146   {
147     Matcher JavaDoc m = macro.getMacroPattern().matcher(quotes.getReplacedSql());
148
149     if (m.find())
150     { // There is a match
151
StringBuffer JavaDoc sql = null;
152       int[] quoteIdxs = quotes.getQuoteIdxs();
153       int[] doubleQuoteIdxs = quotes.getDoubleQuoteIdxs();
154
155       do
156       {
157         if (shouldReplaceMacro(m.start(), quoteIdxs)
158             && shouldReplaceMacro(m.start(), doubleQuoteIdxs))
159         {
160           if (sql == null)
161             sql = new StringBuffer JavaDoc(quotes.getReplacedSql().length());
162           m.appendReplacement(sql, macro.generateMacroValue(currentClock));
163         }
164       }
165       while (m.find());
166       if (sql != null)
167       {
168         m.appendTail(sql);
169         quotes.setReplacedSql(sql.toString());
170       }
171     }
172     return quotes;
173   }
174
175   /**
176    * Process to the macro replacement in the given string
177    *
178    * @param sql the string to process
179    * @return the string with the macros replaced
180    */

181   private String JavaDoc replaceMacrosInString(String JavaDoc sql)
182   {
183     QuoteIndicesAndReplacedSql sqlWithQuotes = new QuoteIndicesAndReplacedSql(
184         sql);
185
186     long currentClock = System.currentTimeMillis();
187     for (Iterator JavaDoc iter = macros.iterator(); iter.hasNext();)
188     {
189       AbstractMacro macro = (AbstractMacro) iter.next();
190       sqlWithQuotes = processMacro(sqlWithQuotes, macro, currentClock);
191     }
192
193     return sqlWithQuotes.getReplacedSql();
194   }
195
196   /**
197    * Retrieve all the indexes of quotes in the string
198    *
199    * @param sql the original query
200    * @return an array of int corresponding to the quote indexes
201    */

202   private int[] getQuoteIndexes(String JavaDoc sql)
203   {
204     ArrayList JavaDoc list = new ArrayList JavaDoc();
205     for (int i = 0; i < sql.length(); i++)
206     {
207       switch (sql.charAt(i))
208       {
209         case '\'' :
210           list.add(new Integer JavaDoc(i));
211           break;
212         case '\\' :
213           i++;
214           break;
215         default :
216           break;
217       }
218     }
219     int[] intList = new int[list.size()];
220     for (int i = 0; i < intList.length; i++)
221       intList[i] = ((Integer JavaDoc) list.get(i)).intValue();
222     return intList;
223   }
224
225   /**
226    * Retrieve all the indexes of double-quotes in the string
227    *
228    * @param sql the original query
229    * @return an array of int corresponding to the double quote indexes
230    */

231   private int[] getDoubleQuoteIndexes(String JavaDoc sql)
232   {
233     ArrayList JavaDoc list = new ArrayList JavaDoc();
234     for (int i = 0; i < sql.length(); i++)
235     {
236       switch (sql.charAt(i))
237       {
238         case '"' :
239           list.add(new Integer JavaDoc(i));
240           break;
241         case '\\' :
242           i++;
243           break;
244         default :
245           break;
246       }
247     }
248     int[] intList = new int[list.size()];
249     for (int i = 0; i < intList.length; i++)
250       intList[i] = ((Integer JavaDoc) list.get(i)).intValue();
251     return intList;
252   }
253
254   /**
255    * Should we replace a macro situated at index idx, knowing that the quotes
256    * are at indexes list
257    *
258    * @param idx the index of the macro
259    * @param list the indexes of quotes
260    * @return <code>true</code> if we should change the macro,
261    * <code>false</code> if the macro is within a string
262    */

263   private boolean shouldReplaceMacro(int idx, int[] list)
264   {
265     int count = 0;
266     while (count < list.length && list[count] < idx)
267     {
268       count++;
269     }
270     return count % 2 == 0;
271   }
272
273   private class QuoteIndicesAndReplacedSql
274   {
275     private int[] quoteIdxs;
276     private int[] doubleQuoteIdxs;
277     private String JavaDoc replacedSql;
278     private boolean requiresQuoteComputation;
279
280     /**
281      * Creates a new <code>QuoteIndicesAndReplacedSql</code> object
282      *
283      * @param sql the sql to parse
284      */

285     public QuoteIndicesAndReplacedSql(String JavaDoc sql)
286     {
287       replacedSql = sql;
288       requiresQuoteComputation = true;
289     }
290
291     private void computeQuotes()
292     {
293       quoteIdxs = getQuoteIndexes(replacedSql);
294       doubleQuoteIdxs = getDoubleQuoteIndexes(replacedSql);
295       requiresQuoteComputation = false;
296     }
297
298     /**
299      * Returns the doubleQuoteIdxs value.
300      *
301      * @return Returns the doubleQuoteIdxs.
302      */

303     public final int[] getDoubleQuoteIdxs()
304     {
305       if (requiresQuoteComputation)
306         computeQuotes();
307       return doubleQuoteIdxs;
308     }
309
310     /**
311      * Returns the quoteIdxs value.
312      *
313      * @return Returns the quoteIdxs.
314      */

315     public final int[] getQuoteIdxs()
316     {
317       if (requiresQuoteComputation)
318         computeQuotes();
319       return quoteIdxs;
320     }
321
322     /**
323      * Returns the replacedSql value.
324      *
325      * @return Returns the replacedSql.
326      */

327     public final String JavaDoc getReplacedSql()
328     {
329       return replacedSql;
330     }
331
332     /**
333      * Sets the replacedSql value.
334      *
335      * @param replacedSql The replacedSql to set.
336      */

337     public final void setReplacedSql(String JavaDoc replacedSql)
338     {
339       this.replacedSql = replacedSql;
340       requiresQuoteComputation = true;
341     }
342   }
343 }
Popular Tags