/************************************************************************* * (c) Copyright 2017 Hewlett Packard Enterprise Development Company LP * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. ************************************************************************/ package com.eucalyptus.cassandra.common.util; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.text.ParseException; import java.util.List; import com.google.common.collect.Lists; /** * Utility class for working with CQL */ public class CqlUtil { private static final char SINGLE_QUOTE = '\''; private static final char DOUBLE_QUOTE = '"'; private static final char DASH_COMMENT = '-'; private static final char SLASH_COMMENT = '/'; private static final char STAR_COMMENT = '*'; private static final char EOL = '\n'; private static final char EOS = ';'; /** * Split a cql script into statements removing comments and whitespace. */ public static List<String> splitCql( final String cql ) throws ParseException { final List<String> statements = Lists.newArrayList( ); final LineNumberReader reader = new LineNumberReader( new StringReader( cql ) ); // cql allows /**/ multiline comments without nesting and // -- single line comments // string quoting can be ' or " and quotes are escaped with a repeated quote char int character; int charCount = -1; try { final StringBuilder statement = new StringBuilder( 512 ); boolean inquote = false; boolean lastinquote = false; char quotechar = SINGLE_QUOTE; boolean incomment = false; int commenttype = DASH_COMMENT; int last = 0; while ( ( character = reader.read( ) ) > 0 ) { charCount++; if ( inquote && last == quotechar ) { // check for end of quote or escaped quote if ( character == quotechar && lastinquote ) { last = character; lastinquote = false; continue; } else if ( lastinquote ) { inquote = false; } } if ( inquote ) { lastinquote = true; statement.append( (char)character ); } else { lastinquote = false; if ( incomment ) { // check for comment end switch ( character ) { case EOL: if ( commenttype == SLASH_COMMENT || commenttype == DASH_COMMENT ) { last = character; incomment = false; continue; } break; case SLASH_COMMENT: if ( commenttype == STAR_COMMENT && last == STAR_COMMENT ) { last = character; incomment = false; continue; } break; } } else { // handle statement text, detect start of comment or quote char commentIf = 0; switch ( character ) { case SINGLE_QUOTE: case DOUBLE_QUOTE: inquote = true; quotechar = (char)character; break; case DASH_COMMENT: commentIf = DASH_COMMENT; break; case SLASH_COMMENT: commentIf = SLASH_COMMENT; break; case STAR_COMMENT: commentIf = SLASH_COMMENT; break; case EOS: statements.add( statement.toString( ).trim( ) ); statement.setLength( 0 ); last = character; continue; } if ( commentIf != 0 && commentIf == last ) { incomment = true; commenttype = character; last = character; statement.setLength( statement.length( ) - 1 ); continue; } statement.append( (char)character ); } } last = character; } // check exit state if ( inquote ) { throw new ParseException( "Unterminated quoted string " + quotechar + ", line " + reader.getLineNumber(), charCount ); } if ( incomment && commenttype == STAR_COMMENT ) { throw new ParseException( "Unterminated comment, line " + reader.getLineNumber(), charCount ); } if ( !statement.toString( ).trim( ).isEmpty( ) ) { throw new ParseException( "Unterminated statement: " + statement.toString( ).trim( ) + ", line " + reader.getLineNumber(), charCount ); } } catch ( IOException e ) { throw (ParseException) new ParseException( "Error reading data, line " + reader.getLineNumber(), charCount ).initCause( e ); } return statements; } }