001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Emmanuel Cecchet.
020: * Contributor(s): ______________________.
021: */package org.continuent.sequoia.controller.backend.rewriting;
022:
023: import java.util.Hashtable;
024: import java.util.StringTokenizer;
025:
026: /**
027: * This class defines a PatternRewritingRule
028: *
029: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
030: * @version 1.0
031: */
032: public class PatternRewritingRule extends AbstractRewritingRule {
033: private char tokenDelimiter = '?';
034: private String[] patternArray;
035: private String[] rewriteArray;
036:
037: /**
038: * Creates a new <code>PatternRewritingRule.java</code> object
039: *
040: * @param queryPattern SQL pattern to match
041: * @param rewrite rewritten SQL query
042: * @param caseSensitive true if matching is case sensitive
043: * @param stopOnMatch true if rewriting must stop after this rule if it
044: * matches.
045: */
046: public PatternRewritingRule(String queryPattern, String rewrite,
047: boolean caseSensitive, boolean stopOnMatch) {
048: super (queryPattern, rewrite, caseSensitive, stopOnMatch);
049:
050: // Parse queryPattern and rewrite to extract the parameters ?1 ?2 ...
051: StringTokenizer patternTokenizer = new StringTokenizer(
052: queryPattern, String.valueOf(tokenDelimiter), true);
053: patternArray = new String[patternTokenizer.countTokens()];
054: int i = 0;
055: try {
056: do {
057: patternArray[i] = patternTokenizer.nextToken();
058: if (patternArray[i].charAt(0) == tokenDelimiter) { // We found a delimiter (?)
059: String nextToken = patternTokenizer.nextToken();
060: // Add the parameter number (only works with 1 digit parameters)
061: //For example ?124 will be recognized as ?1
062: patternArray[i] += nextToken.charAt(0);
063: i++;
064: if (nextToken.length() > 1)
065: // This is the next token
066: patternArray[i] = nextToken.substring(1);
067: }
068: i++;
069: } while (patternTokenizer.hasMoreTokens());
070: } catch (RuntimeException e) {
071: throw new RuntimeException("Malformed query pattern: "
072: + queryPattern);
073: }
074: StringTokenizer rewriteTokenizer = new StringTokenizer(rewrite,
075: String.valueOf(tokenDelimiter), true);
076: rewriteArray = new String[rewriteTokenizer.countTokens()];
077: i = 0;
078: try {
079: do {
080: rewriteArray[i] = rewriteTokenizer.nextToken();
081: if (rewriteArray[i].charAt(0) == tokenDelimiter) { // We found a delimiter (?)
082: String nextToken = rewriteTokenizer.nextToken();
083: // Add the parameter number (only works with 1 digit parameters)
084: //For example ?124 will be recognized as ?1
085: rewriteArray[i] += nextToken.charAt(0);
086: i++;
087: if (nextToken.length() > 1)
088: // This is the next token
089: rewriteArray[i] = nextToken.substring(1);
090: }
091: i++;
092: } while (rewriteTokenizer.hasMoreTokens());
093: } catch (RuntimeException e1) {
094: throw new RuntimeException("Malformed rewrite element: "
095: + rewrite);
096: }
097: }
098:
099: /**
100: * @see org.continuent.sequoia.controller.backend.rewriting.AbstractRewritingRule#rewrite(java.lang.String)
101: */
102: public String rewrite(String sqlQuery) {
103: Hashtable tokens = null; // Parameters value in the query
104: String lastParameter = null;
105: String currentToken;
106: int oldIndex = 0;
107: int newIndex = 0;
108:
109: // Check for match and collect parameters into tokens
110: for (int i = 0; i < patternArray.length; i++) {
111: currentToken = patternArray[i];
112: if (currentToken == null)
113: break; // Last token was a parameter
114: if (currentToken.charAt(0) == tokenDelimiter) { // A new parameter is expected
115: lastParameter = currentToken;
116: continue;
117: }
118: // Here is the value of the parameter
119: newIndex = sqlQuery.indexOf(currentToken, oldIndex);
120: if (newIndex == -1) { // No match
121: hasMatched = false;
122: return sqlQuery;
123: }
124:
125: if (lastParameter != null) { // Add the parameter value
126: if (tokens == null)
127: tokens = new Hashtable();
128: tokens.put(lastParameter, sqlQuery.substring(oldIndex,
129: newIndex));
130: }
131: oldIndex = newIndex + currentToken.length();
132: }
133: // Last parameter
134: if (newIndex < sqlQuery.length()) {
135: if (tokens != null) {
136: if (tokens.containsKey(lastParameter)) { // No match on the end of the pattern
137: hasMatched = false;
138: return sqlQuery;
139: } else
140: tokens.put(lastParameter, sqlQuery
141: .substring(oldIndex));
142: }
143: // Here, we probably had a match without parameters. What's the point?
144: }
145:
146: hasMatched = true;
147:
148: StringBuffer rewrittenQuery = new StringBuffer();
149: for (int i = 0; i < rewriteArray.length; i++) {
150: currentToken = rewriteArray[i];
151: if (currentToken == null)
152: break; // Last token was a parameter
153: if (currentToken.charAt(0) != tokenDelimiter)
154: rewrittenQuery.append(currentToken);
155: else
156: rewrittenQuery.append(tokens.get(currentToken));
157: }
158: return rewrittenQuery.toString();
159: }
160: }
|