All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.impetus.kundera.query.KunderaQueryParser Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/*******************************************************************************
 * * Copyright 2012 Impetus Infotech.
 *  *
 *  * Licensed under the Apache License, Version 2.0 (the "License");
 *  * you may not use this file except in compliance with the License.
 *  * You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS,
 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  * See the License for the specific language governing permissions and
 *  * limitations under the License.
 ******************************************************************************/
package com.impetus.kundera.query;

import java.util.StringTokenizer;

/**
 * Parser for handling JPQL Single-String queries. Takes a JPQLQuery and the
 * query string and parses it into its constituent parts, updating the JPQLQuery
 * accordingly with the result that after calling the parse() method the
 * JPQLQuery is populated.
 * 
 * 
 * SELECT [ {result} ]
 * [FROM {candidate-classes} ]
 * [WHERE {filter}]
 * [GROUP BY {grouping-clause} ]
 * [HAVING {having-clause} ]
 * [ORDER BY {ordering-clause}]
 * e.g SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1
 * 
* * @author animesh.kumar */ public class KunderaQueryParser { /** The JPQL query to populate. */ private KunderaQuery query; /** The single-string query string. */ private String queryString; /** * Record of the keyword currently being processed, so we can check for out * of order keywords. */ // private int keywordPosition = -1; /** * Constructor for the Single-String parser. * * @param query * The query * @param queryString * The Single-String query */ public KunderaQueryParser(KunderaQuery query, String queryString) { this.query = query; this.queryString = queryString; } /** * Method to parse the Single-String query. */ public final void parse() { new Compiler(new Parser(queryString)).compile(); } /** * Method to detect whether this token is a keyword for JPQL Single-String. * * @param token * The token * * @return Whether it is a keyword */ private boolean isKeyword(String token) { // Compare the passed token against the provided keyword list, or their // lowercase form for (int i = 0; i < KunderaQuery.SINGLE_STRING_KEYWORDS.length; i++) { if (token.equalsIgnoreCase(KunderaQuery.SINGLE_STRING_KEYWORDS[i])) { return true; } } return false; } /** * Compiler to process keywords contents. In the query the keywords often * have content values following them that represent the constituent parts * of the query. This takes the keyword and sets the constituent part * accordingly. */ private class Compiler { /** The tokenizer. */ private Parser tokenizer; // Temporary variable since grouping clause is made up of GROUP BY ... // HAVING ... /** The grouping clause. */ private String groupingClause; /** * Instantiates a new compiler. * * @param tokenizer * the tokenizer */ Compiler(Parser tokenizer) { this.tokenizer = tokenizer; } /** * Compile. */ private void compile() { // if it is not an update statement if (!compileUpdate()) { compileSelectOrDelete(); } // any keyword after compiling the SELECT is an error String keyword = tokenizer.parseKeyword(); if (keyword != null) { if (isKeyword(keyword)) { throw new JPQLParseException("Out of order keyword " + keyword + ", entity alias must not be any reserved keyword."); } } } /** * Compile update. * * @return true, if successful */ private boolean compileUpdate() { if (tokenizer.parseKeywordIgnoreCase("UPDATE")) { query.setIsDeleteUpdate(true); compileFrom(); if (tokenizer.parseKeywordIgnoreCase("SET")) { compileUpdateClause(); } compilewhereClause(); return true; } return false; } /** * Compile select. */ private void compileSelectOrDelete() { if (!(tokenizer.parseKeywordIgnoreCase("SELECT"))) { if (tokenizer.parseKeywordIgnoreCase("DELETE")) { query.setIsDeleteUpdate(true); } } compileResult(); if (tokenizer.parseKeywordIgnoreCase("FROM")) { compileFrom(); } compilewhereClause(); } /** * Compilewhere clause. */ private void compilewhereClause() { if (tokenizer.parseKeywordIgnoreCase("WHERE")) { compileWhere(); } if (tokenizer.parseKeywordIgnoreCase("GROUP BY")) { compileGroup(); } if (tokenizer.parseKeywordIgnoreCase("HAVING")) { compileHaving(); } if (groupingClause != null) { query.setGrouping(groupingClause); } if (tokenizer.parseKeywordIgnoreCase("ORDER BY")) { compileOrder(); } } /** * Compile result. */ private void compileResult() { String content = tokenizer.parseContent(); String[] result = null; int count = 0; // content may be empty if (content.length() > 0) { StringTokenizer stringTokenizer = new StringTokenizer(content, ","); result = new String[stringTokenizer.countTokens() + 1]; while (stringTokenizer.hasMoreTokens()) { String property = stringTokenizer.nextToken(); if (property.indexOf(".") > 0) { result[0] = property.substring(0, property.indexOf(".")); String fieldName = property.substring(property.indexOf(".") + 1, property.length()); if (fieldName == null || fieldName.isEmpty()) { throw new JPQLParseException( "You have not given any column name after . ,Column name should not be empty"); } if (result[count] == null) { throw new JPQLParseException("Bad query format"); } result[++count] = fieldName; } else { if (count > 0) { throw new JPQLParseException("Bad query format"); } result[count] = content; count++; } } query.setResult(result); } } /** * Compile from. */ private void compileFrom() { String content = tokenizer.parseContent(); // content may be empty if (content.length() > 0) { query.setFrom(content); } } /** * Compile from. */ private void compileUpdateClause() { String content = tokenizer.parseContent(); // content may be empty if (content.length() > 0) { String[] colArr = tokenizeColumn(content); addUpdateClause(colArr); } } /** * Tokenize column along with it's value using "," as tokenizer * * @param content * content * @return array of tokenized tuple. */ private String[] tokenizeColumn(String content) { StringTokenizer tokenizer = new StringTokenizer(content, ","); String[] columnArr = tokenizer.countTokens() > 0 ? new String[tokenizer.countTokens()] : null; int count = 0; while (tokenizer.hasMoreTokens()) { columnArr[count++] = tokenizer.nextToken(); } return columnArr; } private void addUpdateClause(final String[] clauseArr) { for (String columnTuple : clauseArr) { StringTokenizer tokenizer = new StringTokenizer(columnTuple, "."); String value = getTokenizedValue(tokenizer); StringTokenizer token = new StringTokenizer(value, "="); while (token.hasMoreTokens()) { query.addUpdateClause(token.nextToken(), token.nextToken().trim()); } } } private String getTokenizedValue(StringTokenizer tokenizer) { String value = null; while (tokenizer.hasMoreTokens()) { value = tokenizer.nextToken(); } return value; } /** * Compile where. */ private void compileWhere() { String content = tokenizer.parseContent(); // content cannot be empty if (content.length() == 0) { throw new JPQLParseException("keyword without value[WHERE]"); } query.setFilter(content); } /** * Compile group. */ private void compileGroup() { String content = tokenizer.parseContent(); // content cannot be empty if (content.length() == 0) { throw new JPQLParseException("keyword without value: GROUP BY"); } groupingClause = content; } /** * Compile having. */ private void compileHaving() { String content = tokenizer.parseContent(); // content cannot be empty if (content.length() == 0) { throw new JPQLParseException("keyword without value: HAVING"); } if (groupingClause != null) { groupingClause = groupingClause.trim() + content; } else { groupingClause = content; } } /** * Compile order. */ private void compileOrder() { String content = tokenizer.parseContent(); // content cannot be empty if (content.length() == 0) { throw new JPQLParseException("keyword without value: ORDER BY"); } query.setOrdering(content); } } /** * Tokenizer that provides access to current token. */ private class Parser { /** tokens. */ private final String[] tokens; /** keywords. */ private final String[] keywords; /** current token cursor position. */ private int pos = -1; /** * Constructor. * * @param str * the str */ public Parser(String str) { StringTokenizer tokenizer = new StringTokenizer(str); tokens = new String[tokenizer.countTokens()]; keywords = new String[tokenizer.countTokens()]; int i = 0; while (tokenizer.hasMoreTokens()) { tokens[i++] = tokenizer.nextToken(); } for (i = 0; i < tokens.length; i++) { if (isKeyword(tokens[i])) { keywords[i] = tokens[i]; } else if (i < tokens.length - 1 && isKeyword(tokens[i] + ' ' + tokens[i + 1])) { keywords[i] = tokens[i]; i++; keywords[i] = tokens[i]; } } } /** * Parse the content until a keyword is found. * * @return the content */ public String parseContent() { String content = ""; while (pos < tokens.length - 1) { pos++; if (isKeyword(tokens[pos])) { pos--; break; } else if (pos < tokens.length - 1 && isKeyword(tokens[pos] + ' ' + tokens[pos + 1])) { pos--; break; } else { if (content.length() == 0) { content = tokens[pos]; } else { content += " " + tokens[pos]; } } } return content; } /** * Parse the next token looking for a keyword. The cursor position is * skipped in one tick if a keyword is found * * @param keyword * the searched keyword * * @return true if the keyword */ public boolean parseKeywordIgnoreCase(String keyword) { if (pos < tokens.length - 1) { pos++; if (keywords[pos] != null) { if (keywords[pos].equalsIgnoreCase(keyword)) { return true; } if (keyword.indexOf(' ') > -1) { if ((keywords[pos] + ' ' + keywords[pos + 1]).equalsIgnoreCase(keyword)) { pos++; return true; } } } pos--; } return false; } /** * Parse the next token looking for a keyword. The cursor position is * skipped in one tick if a keyword is found * * @return the parsed keyword or null */ public String parseKeyword() { if (pos < tokens.length - 1) { pos++; if (keywords[pos] != null) { return keywords[pos]; } pos--; } return null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy