alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * MM JDBC Drivers for MySQL
 *
 * $Id: EscapeProcessor.java,v 1.2 1998/08/25 00:53:47 mmatthew Exp $
 *
 * Copyright (C) 1998 Mark Matthews <mmatthew@worldserver.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * See the COPYING file located in the top-level-directory of
 * the archive of this library for complete text of license.
 */

/**
 * EscapeProcessor performs all escape code processing as outlined
 * in the JDBC spec by JavaSoft.
 *
 * @author Mark Matthews <mmatthew@worldserver.com>
 * @version $Id$
 */

package org.gjt.mm.mysql;

import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Stack;

class EscapeProcessor
{	
    /**
     * Escape process one string
     *
     * @param SQL the SQL to escape process.
     * @return the SQL after it has been escape processed.      
     */
    
    public synchronized String escapeSQL(String SQL) throws java.sql.SQLException
    {
	boolean replaceEscapeSequence = false;
	String EscapeSequence = null;
	StringBuffer NewSQL = new StringBuffer();
	
	boolean inBraces = false;
	boolean inQuotes = false;

        boolean escaped = false;
        boolean unrecognizedEscape = false;
        String OpeningBrace="";
	
	if (SQL == null) {
	    return null;
	}

	/*
	 * Short circuit this code if we don't have a matching pair of
	 * "{}". - Suggested by Ryan Gustafason
	 */
	
	int begin_brace = SQL.indexOf("{");
	int next_end_brace = SQL.indexOf("}", begin_brace);
	
	if (next_end_brace == -1) {
		return SQL;
	}
		            
	PushBackTokenizer PBT = new PushBackTokenizer(SQL, "{}'", true);

	while (PBT.hasMoreTokens()) {
	    String Token = PBT.nextToken();
	    
	    if (Token.startsWith("{")) {
                if (escaped) {
		    NewSQL.append(Token);
		    escaped = false;
                } else {
		    NewSQL.append(OpeningBrace);
		    inBraces = true;
		    OpeningBrace = Token;
                }
	    }
	    else if (Token.startsWith("}")) {
                if (escaped) {
		    NewSQL.append(Token);
		    escaped = false;
                } else {
		    if (inBraces == false) {
			NewSQL.append(Token);
		    } else {
			inBraces = false;
			if (!OpeningBrace.equals("")) {
			    NewSQL.append(OpeningBrace);
			    NewSQL.append(Token);
			    OpeningBrace="";
			}
			if (unrecognizedEscape) {
			    NewSQL.append(Token);
			    unrecognizedEscape = false;
			}
		    }
                }
	    }
	    else if (Token.startsWith("'")) {
		if (escaped) {
		    NewSQL.append(Token);
		    escaped = false;
		}
		else {
		    NewSQL.append(Token);
		    inQuotes = !inQuotes;
		}
	    }
	    else {
		if (inBraces && !inQuotes) {
		    
		    /*
		     * Process the escape code
		     */
		    
		    if (Token.startsWith("escape")) { 
			try {
			    StringTokenizer ST = new StringTokenizer(Token, " '");
			    ST.nextToken(); // eat the "escape" token
			    EscapeSequence = ST.nextToken();
			    if (EscapeSequence.length() < 3) {
				throw new java.sql.SQLException("Syntax error for escape sequence '" + Token + "'", "42000"); 
			    }
			    EscapeSequence = EscapeSequence.substring( 1, EscapeSequence.length() - 1);
			    
			    replaceEscapeSequence = true;
			}
			catch (java.util.NoSuchElementException E) {
			    throw new java.sql.SQLException("Syntax error for escape sequence '" + Token + "'", "42000");
			}                                                     
		    }
		    else if (Token.startsWith("fn")) {
			
			// just pass functions right to the DB
			int start_pos = Token.indexOf("fn ") + 3;   
                        int end_pos = Token.length();               
			
                        NewSQL.append(Token.substring(start_pos, end_pos));
			
			try {
			    NewSQL.append(parseComplexArgument(PBT));
			}
			catch (java.util.NoSuchElementException NSE) {
			    throw new java.sql.SQLException("Syntax error for FN escape code", "42000");
			}
		    }
		    else if (Token.startsWith("d")) {
			
			String Argument = "";
			
			try {
			    Argument = parseArgument(PBT);
			}
			catch (java.util.NoSuchElementException NSE) {
			    throw new java.sql.SQLException("Illegal argument for DATE escape code '" + Argument + "'", "42000");
			}
			
			try {
			    StringTokenizer ST = new StringTokenizer(Argument, " -");
			    
			    String YYYY = ST.nextToken();
			    String MM   = ST.nextToken();
			    String DD   = ST.nextToken();
			    
			    String DateString = "'" + YYYY + "-" + MM + "-" + DD + "'";
			    
			    NewSQL.append(DateString);
			}
			catch (java.util.NoSuchElementException E) {
			    throw new java.sql.SQLException("Syntax error for DATE escape sequence '" + Argument + "'", "42000");
			}
		    }
		    else if (Token.startsWith("ts")) {

			String Argument = "";
			
			try {
			    Argument = parseArgument(PBT);
			}
			catch (java.util.NoSuchElementException NSE) {
			    throw new java.sql.SQLException("Illegal argument for TIMESTAMP escape code '" + Argument + "'", "42000");
			}
			
			try {
			    StringTokenizer ST = new StringTokenizer(Argument, " .-:");
			    
			    String YYYY = ST.nextToken();
			    String MM   = ST.nextToken();                     
			    String DD   = ST.nextToken();                      
			    String HH   = ST.nextToken();                     
			    String Mm   = ST.nextToken(); 	    
			    String SS   = ST.nextToken();
		
				
			    /*
			     * For now, we get the fractional seconds
			     * part, but we don't use it, as MySQL doesn't
			     * support it in it's TIMESTAMP data type
			     */

			    String F = "";
							
			    if (ST.hasMoreTokens()) {
				F = ST.nextToken();
			    }
							
			    /*
			     * Use the full format because number format
			     * will not work for "between" clauses.
			     *
			     * Ref. Mysql Docs
			     * 
			     * You can specify DATETIME, DATE and TIMESTAMP values 
			     * using any of a common set of formats: 
			     * 
			     * As a string in either 'YYYY-MM-DD HH:MM:SS' or
			     * 'YY-MM-DD HH:MM:SS' format. 
			     *
			     * Thanks to Craig Longman for pointing out this bug
			     */							
			    NewSQL.append("'").append(YYYY).append("-").append(MM).append("-").append(DD).append(" ").append(HH).append(":").append(Mm).append(":").append(SS).append("'");
			}
			catch (java.util.NoSuchElementException E) {
			    throw new java.sql.SQLException("Syntax error for TIMESTAMP escape sequence '" + Argument + "'", "42000");
			}
		    }
		    else if (Token.startsWith("t")) {

			String Argument = "";
       
			try {
			    Argument = parseArgument(PBT);
			}
			catch (java.util.NoSuchElementException NSE) {
			    throw new java.sql.SQLException("Illegal argument for TIME escape code '" + Argument + "'", "42000");
			}

			try {
			    StringTokenizer ST = new StringTokenizer(Argument, " ':");
			                                                 
			    String HH   = ST.nextToken();                     
			    String MM   = ST.nextToken();      
			    String SS   = ST.nextToken();		    
			    
			    String TimeString = "'" + HH + ":" + MM + ":" +SS + "'";
							                      
			    NewSQL.append(TimeString);
			}
			catch (java.util.NoSuchElementException E) {
			    throw new java.sql.SQLException("Syntax error for escape sequence '" + Argument + "'", "42000");
			}
		    }
		    else if (Token.startsWith("call") ||
			     Token.startsWith("? = call")) {
			throw new java.sql.SQLException("Stored procedures not supported: " + Token, "S1C00");
		    }
		    else if (Token.startsWith("oj")) {
			// MySQL already handles this escape sequence
			// because of ODBC. Cool.

			try {
			    NewSQL.append(parseComplexArgument(PBT));
			}
			catch (java.util.NoSuchElementException NSE) {
			    throw new java.sql.SQLException("Syntax error for OJ escape code", "42000");
			}
		    }
		    else {
                        NewSQL.append(OpeningBrace);
                        NewSQL.append(Token);
                        unrecognizedEscape = true;
                        OpeningBrace = "";
		    }  	
                    OpeningBrace="";
		}
		else {
		    NewSQL.append(Token);
		}
	    }

	    if (Token.endsWith("\\")) {
		escaped = true;
	    }
	    
	}
        NewSQL.append(OpeningBrace);

	String EscapedSQL = NewSQL.toString();
		            
	if (replaceEscapeSequence) {
	    String CurrentSQL = EscapedSQL;
	    while (CurrentSQL.indexOf(EscapeSequence) != -1) {
		int escapePos = CurrentSQL.indexOf(EscapeSequence);
		String LHS = CurrentSQL.substring(0, escapePos);
		String RHS = CurrentSQL.substring(escapePos + 1, CurrentSQL.length());
		CurrentSQL = LHS + "\\" + RHS;
	    }
	    EscapedSQL = CurrentSQL;      
	}

	// Do we need to do the concatenation operator?
	if (EscapedSQL.indexOf("||") != -1) {
	    EscapedSQL = doConcat(EscapedSQL);
	}

	return EscapedSQL;
    }
          
    /**
     * Do concatenation for the SQL operator "||" because
     * MySQL doesn't support it, but some IDEs (i.e. VisualAge)
     * use it.
     *
     * @param SQL A String to do concatenation operations on
     */

    static String doConcat(String SQL)
    {
	Vector TokenList = new Vector();
	StringTokenizer ST = new StringTokenizer(SQL, " '", true);
	
	boolean inquotes = false;
	StringBuffer QuotedString = null;

	while (ST.hasMoreTokens()) {
	    String T = ST.nextToken();
	    if (T.equals("'")) {
		if (inquotes == true) {
		    inquotes = false;
		    Token Tok = new Token(QuotedString.toString(), true);
		    TokenList.addElement(Tok);
		}
		else {
		    inquotes = true;
		    QuotedString = new StringBuffer();
		}
	    }
	    else {
		if (inquotes) {
		    QuotedString.append(T);
		}
		else {
		    Token Tok = new Token(T, false);

		    TokenList.addElement(Tok);
		}
	    }
	}

	// Now go through and find what we need to concatenate

	int pos = 0;

	int length = TokenList.size();
	Stack ToDo = new Stack();

	while (pos < length) {
	    Token T1 = (Token)TokenList.elementAt(pos);
	    if (T1.Value.equals("||")) {
		Token Pre = (Token)ToDo.pop();
		pos++;
		Token Post = (Token)TokenList.elementAt(pos);
	
		Token Concat = new Token(Pre.Value + Post.Value, true);
		ToDo.push(Concat);
		pos++;
	    }
	    else {
		ToDo.push(T1);
		pos++;
	    }
		    
	}

	length = ToDo.size();
	StringBuffer NewQuery = new StringBuffer();

	for (int i = 0; i < length; i++) {
	    Token T = (Token)ToDo.elementAt(i);
	    if (T.quoted) {
		NewQuery.append("'");
		NewQuery.append(T.Value);
		NewQuery.append("'");
	    }
	    else {
		NewQuery.append(T.Value);
	    }
	}
	
	return NewQuery.toString();
    }


    /**
     * Given the current tokenizer, assemble an escape code argument
     */

    private final static String parseArgument(PushBackTokenizer Tokenizer)
    throws java.util.NoSuchElementException
    {
	StringBuffer Argument = new StringBuffer();
	boolean done_parsing = false;
	boolean seen_one_quote = false;
	
	while (!done_parsing) {
	    String Tok = Tokenizer.nextToken();
	    if (Tok.equals("'")) {
		if (seen_one_quote) {
		    done_parsing = true;
		}
		else {
		    seen_one_quote = true;
		}
	    }
	    else {
		Argument.append(Tok);
	    }
	}
	
	return Argument.toString();
    }

    
    /**
     * Given the current tokenizer, assemble an escape code argument
     */

    private final static String parseComplexArgument(PushBackTokenizer Tokenizer)
	throws java.util.NoSuchElementException
    {
	StringBuffer Argument = new StringBuffer();

	boolean done_parsing = false;
	boolean in_quotes = false;
	
	while (!done_parsing) {
	    String Tok = Tokenizer.nextToken();
    
	    if (Tok.equals("'")) {
		in_quotes = !in_quotes;
		Argument.append(Tok);
	    }
	    else {
		if (!in_quotes && Tok.equals("}")) {
		    done_parsing = true;
		    Tokenizer.pushBack();
		}
		else {
		    Argument.append(Tok);
		}
	    }
	}
	
	return Argument.toString();
    }
};
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.