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        /**
030         * Value indicating the sql has no end-delimiter like i.e. semicolon
031         */
032        public static final  int NO_END = -1; 
033        
034        /**
035         * Check if the given sql line contains an end of command ';'
036         * Please note that we do <em>not</em> fully parse the SQL, 
037         * so if we get a malformed statement, we cannot detect it.
038         * 
039         * @param line to parse
040         * @param delimiter 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        {
046            // / * * / comments
047            boolean isComment = false;
048            
049            boolean isAlphaDelimiter = StringUtils.isAlpha( delimiter );
050            
051            if ( line == null || line.length() == 0 )
052            {
053                return NO_END;
054            }
055            
056            int pos = 0;
057            
058            do 
059            {
060                if ( isComment )
061                {
062                    if ( line.startsWith( "*/", pos ) )
063                    {
064                        isComment = false;
065                    }
066                    else
067                    {
068                        pos++;
069                        continue;
070                    }
071                }
072                
073                if ( line.startsWith( "/*", pos ) )
074                {
075                    isComment = true;
076                    pos += 2;
077                    continue;
078                }
079                
080                if ( line.startsWith( "--", pos ) )
081                {
082                    return NO_END;
083                }
084                
085                if (  line.startsWith( "'", pos ) || line.startsWith( "\"", pos ) )
086                {
087                    String quoteChar = "" + line.charAt( pos );
088                    String quoteEscape = "\\" + quoteChar;
089                    pos++;
090                    
091                    if ( line.length() <= pos )
092                    {
093                        return NO_END;
094                    }
095                    
096                    do 
097                    {
098                        if ( line.startsWith( quoteEscape, pos ) )
099                        {
100                            pos += 2;
101                        }
102                    } while ( !line.startsWith( quoteChar, pos++ ) );
103                    
104                    continue;
105                }
106    
107                if ( line.startsWith( delimiter, pos ) )
108                {
109                    if ( isAlphaDelimiter )
110                    {
111                        // check if delimiter is at start or end of line, surrounded
112                        // by non-alpha character
113                        if (  ( pos == 0 || !isAlpha( line.charAt( pos - 1 ) ) ) 
114                                        && ( line.length() == pos + delimiter.length() 
115                                        || !isAlpha( line.charAt( pos + delimiter.length() ) ) ) ) 
116                        {
117                            return pos + delimiter.length();
118                        }
119                    }
120                    else
121                    {
122                        return pos + delimiter.length();
123                    }
124                }
125                
126                pos++;
127                
128            } while ( line.length() >= pos );
129                
130            return NO_END;
131        }
132        
133        /**
134         * @param c the char to check
135         * @return <code>true</code> if the given character is either a lower or an upperchase alphanumerical character
136         */
137        private static boolean isAlpha( char c )
138        {
139            return Character.isUpperCase( c ) || Character.isLowerCase( c );
140        }
141        
142    }