001: /**
002: * com.mckoi.database.sql.Util 08 Jul 2000
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database.sql;
024:
025: import com.mckoi.database.Expression;
026: import com.mckoi.database.Operator;
027: import com.mckoi.database.FunctionDef;
028: import com.mckoi.database.Variable;
029: import com.mckoi.database.TableName;
030: import com.mckoi.database.TObject;
031: import com.mckoi.database.TType;
032: import com.mckoi.database.global.*;
033: import com.mckoi.util.BigNumber;
034:
035: /**
036: * Various utility methods for the iterpreter.
037: *
038: * @author Tobias Downer
039: */
040:
041: public class Util {
042:
043: private static TObject ZERO_NUMBER = TObject.intVal(0);
044:
045: /**
046: * Returns the Token as a non quoted reference. For example, a
047: * QUOTED_VARIABLE token will have the first and last '"' character
048: * removed. A QUOTED_DELIMINATED_REF will have " removed in each deliminated
049: * section. For example, '"re1"."re2"."a"' becomes 're1.re2.a" and
050: * '"re1.re2.a"' becomes 're1.re2.a'.
051: */
052: public static String asNonQuotedRef(Token token) {
053: if (token.kind == SQLConstants.QUOTED_VARIABLE) {
054: // Strip " from start and end if a quoted variable
055: return token.image.substring(1, token.image.length() - 1);
056: } else if (token.kind == SQLConstants.QUOTED_DELIMINATED_REF
057: || token.kind == SQLConstants.QUOTEDGLOBVARIABLE) {
058: // Remove all " from the string
059: String image = token.image;
060: StringBuffer b = new StringBuffer();
061: int sz = image.length();
062: for (int i = 0; i < sz; ++i) {
063: char c = image.charAt(i);
064: if (c != '\"') {
065: b.append(c);
066: }
067: }
068: return new String(b);
069: } else {
070: return token.image;
071: }
072: }
073:
074: /**
075: * Converts a Token which is either a STRING_LITERAL, NUMBER_LITERAL or
076: * IDENTIFIER into a Java Object. If 'upper_identifiers' is true then all
077: * identifiers are made upper case before being returned (eg. if the
078: * object returns is a Variable object).
079: */
080: public static Object toParamObject(Token token,
081: boolean upper_identifiers) {
082: if (token.kind == SQLConstants.STRING_LITERAL) {
083: String raw_string = token.image.substring(1, token.image
084: .length() - 1);
085: return TObject.stringVal(escapeTranslated(raw_string));
086: }
087: // else if (token.kind == SQLConstants.NUMBER_LITERAL) {
088: // return TObject.bigNumberVal(BigNumber.fromString(token.image));
089: // }
090: else if (token.kind == SQLConstants.BOOLEAN_LITERAL) {
091: return TObject.booleanVal(token.image
092: .equalsIgnoreCase("true"));
093: } else if (token.kind == SQLConstants.NULL_LITERAL) {
094: return TObject.nullVal();
095: } else if (token.kind == SQLConstants.REGEX_LITERAL) {
096: // Horrible hack,
097: // Get rid of the 'regex' string at the start,
098: String str = token.image.substring(5).trim();
099: return TObject.stringVal(str);
100: } else if (token.kind == SQLConstants.QUOTED_VARIABLE
101: || token.kind == SQLConstants.GLOBVARIABLE
102: || // eg. Part.*
103: token.kind == SQLConstants.IDENTIFIER
104: || token.kind == SQLConstants.DOT_DELIMINATED_REF
105: || token.kind == SQLConstants.QUOTED_DELIMINATED_REF) {
106: String name = asNonQuotedRef(token);
107: // if (token.kind == SQLConstants.QUOTED_VARIABLE) {
108: // name = token.image.substring(1, token.image.length() - 1);
109: // }
110: // else {
111: // name = token.image;
112: // }
113: if (upper_identifiers) {
114: name = name.toUpperCase();
115: }
116: Variable v;
117: int div = name.lastIndexOf(".");
118: if (div != -1) {
119: // Column represents '[something].[name]'
120: // Check if the column name is an alias.
121: String column_name = name.substring(div + 1);
122: // Make the '[something]' into a TableName
123: TableName table_name = TableName.resolve(name
124: .substring(0, div));
125:
126: // Set the variable name
127: v = new Variable(table_name, column_name);
128: } else {
129: // Column represents '[something]'
130: v = new Variable(name);
131: }
132: return v;
133: } else { // Otherwise it must be a reserved word, so just return the image
134: // as a variable.
135: String name = token.image;
136: if (upper_identifiers) {
137: name = name.toUpperCase();
138: }
139: return new Variable(token.image);
140: }
141: }
142:
143: /**
144: * Returns numeric 0
145: */
146: public static TObject zeroNumber() {
147: return ZERO_NUMBER;
148: }
149:
150: /**
151: * Parses a NUMBER_LITERAL Token with a sign boolean.
152: */
153: public static TObject parseNumberToken(Token token, boolean negative) {
154: if (negative) {
155: return TObject.bigNumberVal(BigNumber.fromString("-"
156: + token.image));
157: } else {
158: return TObject.bigNumberVal(BigNumber
159: .fromString(token.image));
160: }
161: }
162:
163: /**
164: * Converts an expression array to an array type that can be added to an
165: * expression.
166: */
167: public static TObject toArrayParamObject(Expression[] arr) {
168: return new TObject(TType.ARRAY_TYPE, arr);
169: }
170:
171: /**
172: * Returns an array of Expression objects as a comma deliminated string.
173: */
174: public static String expressionListToString(Expression[] list) {
175: StringBuffer buf = new StringBuffer();
176: for (int i = 0; i < list.length; ++i) {
177: buf.append(list[i].text().toString());
178: if (i < list.length - 1) {
179: buf.append(", ");
180: }
181: }
182: return new String(buf);
183: }
184:
185: /**
186: * Normalizes the Expression by removing all NOT operators and altering
187: * the expression as appropriate. For example, the expression;
188: * not ((a + b) = c and c = 5)
189: * would be normalized to;
190: * (a + b) <> c or c <> 5
191: */
192: public static Expression normalize(final Expression exp) {
193: // Only normalize if the expression contains a NOT operator.
194: if (exp.containsNotOperator()) {
195: return normalize(exp, false);
196: }
197: return exp;
198: }
199:
200: /**
201: * Normalizes the Expression by removing all NOT operators and altering
202: * the expression as appropriate. For example, the expression;
203: * not ((a + b) = c and c = 5)
204: * would be normalized to;
205: * (a + b) <> c or c <> 5
206: */
207: private static Expression normalize(final Expression exp,
208: final boolean inverse) {
209: if (exp.size() <= 1) {
210: if (inverse) {
211: return standardInverse(exp);
212: } else {
213: return exp;
214: }
215: }
216: final Operator op = (Operator) exp.last();
217: final Expression[] exps = exp.split();
218:
219: if (op.isNot()) {
220: // If the operator is NOT then return the normalized form of the LHS.
221: // We toggle the inverse flag.
222: return normalize(exps[0], !inverse);
223: } else if (op.isNotInversible()) {
224: // If the operator is not inversible, return the expression with a
225: // '= false' if nothing else is possible
226: Expression resolved_expr = new Expression(normalize(
227: exps[0], false), op, normalize(exps[1], false));
228: if (inverse) {
229: return standardInverse(resolved_expr);
230: } else {
231: return resolved_expr;
232: }
233: } else if (op.isLogical()) {
234: // If logical we inverse the operator and inverse the left and right
235: // side of the operator also.
236: if (inverse) {
237: return new Expression(normalize(exps[0], inverse), op
238: .inverse(), normalize(exps[1], inverse));
239: } else {
240: return new Expression(normalize(exps[0], inverse), op,
241: normalize(exps[1], inverse));
242:
243: }
244: } else {
245: // By this point we can assume the operator is naturally inversible.
246: if (inverse) {
247: return new Expression(normalize(exps[0], false), op
248: .inverse(), normalize(exps[1], false));
249: } else {
250: return new Expression(normalize(exps[0], false), op,
251: normalize(exps[1], false));
252: }
253: }
254:
255: }
256:
257: /**
258: * Returns an expression that is (exp) = false which is the natural
259: * inverse of all expressions. This should only be used if the expression
260: * can't be inversed in any other way.
261: */
262: private static Expression standardInverse(Expression exp) {
263: return new Expression(exp, Operator.get("="), new Expression(
264: TObject.booleanVal(false)));
265: }
266:
267: /**
268: * Returns a Function object that represents the name and expression list
269: * (of parameters) of a function. Throws an exception if the function
270: * doesn't exist.
271: */
272: public static FunctionDef resolveFunctionName(String name,
273: Expression[] exp_list) {
274: return new FunctionDef(name, exp_list);
275: }
276:
277: /**
278: * Translate a string with escape codes into a un-escaped Java string. \' is
279: * converted to ', \n is a newline, \t is a tab, \\ is \, etc.
280: */
281: private static String escapeTranslated(String input) {
282: StringBuffer result = new StringBuffer();
283: int size = input.length();
284: boolean last_char_escape = false;
285: boolean last_char_quote = false;
286: for (int i = 0; i < size; ++i) {
287: char c = input.charAt(i);
288: if (last_char_quote) {
289: last_char_quote = false;
290: if (c != '\'') {
291: result.append(c);
292: }
293: } else if (last_char_escape) {
294: if (c == '\\') {
295: result.append('\\');
296: } else if (c == '\'') {
297: result.append('\'');
298: } else if (c == 't') {
299: result.append('\t');
300: } else if (c == 'n') {
301: result.append('\n');
302: } else if (c == 'r') {
303: result.append('\r');
304: } else {
305: result.append('\\');
306: result.append(c);
307: }
308: last_char_escape = false;
309: } else if (c == '\\') {
310: last_char_escape = true;
311: } else if (c == '\'') {
312: last_char_quote = true;
313: result.append(c);
314: } else {
315: result.append(c);
316: }
317: }
318: return new String(result);
319: }
320:
321: }
|