001    package org.codehaus.mojo.sql;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     * http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.codehaus.plexus.util.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)))
099                                && (line.length() == pos + delimiter.length() || !isAlpha(line.charAt(pos
100                                        + delimiter.length())))) {
101                            return pos + delimiter.length();
102                        }
103                    } else {
104                        return pos + delimiter.length();
105                    }
106                }
107    
108                pos++;
109    
110            } while (line.length() >= pos);
111    
112            return NO_END;
113        }
114    
115        /**
116         * @param c
117         *            the char to check
118         * @return <code>true</code> if the given character is either a lower or an upperchase alphanumerical character
119         */
120        private static boolean isAlpha(char c) {
121            return Character.isUpperCase(c) || Character.isLowerCase(c);
122        }
123    
124    }