001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.    
018     */
019    
020    package org.kuali.db;
021    
022    import org.apache.commons.lang.StringUtils;
023    
024    /**
025     * Utility class to split a long sql batch script into single SQL commands.
026     */
027    public class SqlSplitter {
028            /**
029             * Value indicating the sql has no end-delimiter like i.e. semicolon
030             */
031            public static final int NO_END = -1;
032    
033            /**
034             * Check if the given sql line contains an end of command ';' Please note that we do <em>not</em> fully parse the
035             * SQL, so if we get a malformed statement, we cannot detect it.
036             * 
037             * @param line
038             *            to parse
039             * @param delimiter
040             *            which should be used to split SQL commands
041             * @return position after the end character if the given line contains the end of a SQL script,
042             *         {@value SqlSplitter#NO_END} if it doesn't contain an end char.
043             */
044            public static int containsSqlEnd(String line, String delimiter) {
045                    // / * * / comments
046                    boolean isComment = false;
047    
048                    boolean isAlphaDelimiter = StringUtils.isAlpha(delimiter);
049    
050                    if (line == null || line.length() == 0) {
051                            return NO_END;
052                    }
053    
054                    int pos = 0;
055    
056                    do {
057                            if (isComment) {
058                                    if (line.startsWith("*/", pos)) {
059                                            isComment = false;
060                                    } else {
061                                            pos++;
062                                            continue;
063                                    }
064                            }
065    
066                            if (line.startsWith("/*", pos)) {
067                                    isComment = true;
068                                    pos += 2;
069                                    continue;
070                            }
071    
072                            if (line.startsWith("--", pos)) {
073                                    return NO_END;
074                            }
075    
076                            if (line.startsWith("'", pos) || line.startsWith("\"", pos)) {
077                                    String quoteChar = "" + line.charAt(pos);
078                                    String quoteEscape = "\\" + quoteChar;
079                                    pos++;
080    
081                                    if (line.length() <= pos) {
082                                            return NO_END;
083                                    }
084    
085                                    do {
086                                            if (line.startsWith(quoteEscape, pos)) {
087                                                    pos += 2;
088                                            }
089                                    } while (!line.startsWith(quoteChar, pos++));
090    
091                                    continue;
092                            }
093    
094                            if (line.startsWith(delimiter, pos)) {
095                                    if (isAlphaDelimiter) {
096                                            // check if delimiter is at start or end of line, surrounded
097                                            // by non-alpha character
098                                            if ((pos == 0 || !isAlpha(line.charAt(pos - 1))) && (line.length() == pos + delimiter.length() || !isAlpha(line.charAt(pos + delimiter.length())))) {
099                                                    return pos + delimiter.length();
100                                            }
101                                    } else {
102                                            return pos + delimiter.length();
103                                    }
104                            }
105    
106                            pos++;
107    
108                    } while (line.length() >= pos);
109    
110                    return NO_END;
111            }
112    
113            /**
114             * @param c
115             *            the char to check
116             * @return <code>true</code> if the given character is either a lower or an upperchase alphanumerical character
117             */
118            private static boolean isAlpha(char c) {
119                    return Character.isUpperCase(c) || Character.isLowerCase(c);
120            }
121    
122    }