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 }