/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ options { BUILD_NODE_FILES=false; MULTI=true; VISITOR=true; JAVA_UNICODE_ESCAPE = true; STATIC = false; CHOICE_AMBIGUITY_CHECK = 5; OTHER_AMBIGUITY_CHECK = 5; LOOKAHEAD = 1; DEBUG_PARSER = false; DEBUG_LOOKAHEAD = false; DEBUG_TOKEN_MANAGER = false; ERROR_REPORTING = true; UNICODE_INPUT = false; IGNORE_CASE = false; USER_TOKEN_MANAGER = false; USER_CHAR_STREAM = false; BUILD_PARSER = true; BUILD_TOKEN_MANAGER = true; SANITY_CHECK = true; FORCE_LA_CHECK = false; } PARSER_BEGIN(JBossQLParser) package org.jboss.ejb.plugins.cmp.ejbql; import java.io.CharArrayReader; import java.util.ArrayList; import java.util.List; import org.jboss.ejb.plugins.cmp.bridge.EntityBridge; import org.jboss.ejb.plugins.cmp.bridge.CMPFieldBridge; import org.jboss.ejb.plugins.cmp.bridge.CMRFieldBridge; import org.jboss.ejb.plugins.cmp.bridge.FieldBridge; /** * This class parses JBossQL into an abstract syntax tree. * * @author Dain Sundstrom * @author Alex Loubyansky * @version $Revision: 44173 $ */ public class JBossQLParser { private Catalog catalog; private Class[] argumentTypes; private IdentifierManager idManager; private boolean selectDistinct; private String selectPath; public ASTEJBQL parse(Catalog catalog, Class[] argumentTypes, String ejbql) throws ParseException { this.catalog = catalog; token_source.catalog = catalog; this.argumentTypes = argumentTypes; token_source.argumentTypes = argumentTypes; idManager = new IdentifierManager(catalog); token_source.idManager = idManager; String lowerCase = ejbql.toLowerCase(); int fromBeginIndex = indexOf(lowerCase, "from"); if(fromBeginIndex < 0) { throw new ParseException("FROM not found"); } int fromEndIndex = indexOf(lowerCase, "where"); ///////////////////// CHANGED ////////////////////////////// if(fromEndIndex < 0) { fromEndIndex = indexOfOrderBy(lowerCase); } if(fromEndIndex < 0) { fromEndIndex = indexOf(lowerCase, "offset"); } if(fromEndIndex < 0) { fromEndIndex = indexOf(lowerCase, "limit"); } if(fromEndIndex < 0) { fromEndIndex = ejbql.length(); } //////////////////////////////////////////////////////////// // just the from clause char[] from = ejbql.toCharArray(); clear(from, 0, fromBeginIndex); clear(from, fromEndIndex, from.length); // everything except for the from clause char[] sansFrom = ejbql.toCharArray(); clear(sansFrom, fromBeginIndex + 4, fromEndIndex); // From clause ReInit(new CharArrayReader(from)); ASTFrom fromNode = ParseFromClause(); // Everything else ReInit(new CharArrayReader(sansFrom)); ASTEJBQL ejbqlNode = EJBQL(); // replace the dummy from clause in the EJBQL // node with the real from node ejbqlNode.jjtAddChild(fromNode, 1); return ejbqlNode; } /** * Returns the first index of the word (surrounded with whitespaces) in the string * or -1 if the word was not found. */ private static final int indexOf(String ql, String word) { return indexOf(ql, word, 0); } /** * Returns the first index of the word (surrounded with whitespaces) in the string * starting with startInd position or -1 if the word was not found. */ private static final int indexOf(String ql, String word, int startInd) { int i = ql.indexOf(word, startInd); if(i < 0) { return -1; } int qlLength = ql.length(); int wordLength = word.length(); while(i >= 0) { int endInd = i + wordLength; if((i == 0 || Character.isWhitespace(ql.charAt(i - 1))) && (endInd == qlLength || endInd < qlLength && Character.isWhitespace(ql.charAt(endInd)))) { break; } i = ql.indexOf(word, i + 1); } return i; } private static final int indexOfOrderBy(String ql) { int orderInd = indexOf(ql, "order", 0); int byInd = -1; while(orderInd > 0) { if(byInd < orderInd) { byInd = indexOf(ql, "by", orderInd + 5); } if(byInd > 0) { int i = byInd - 1; while(i >= orderInd + 5 && Character.isWhitespace(ql.charAt(i--))); if(i == orderInd + 4) { break; } else { orderInd = indexOf(ql, "order", orderInd + 5); } } else { orderInd = -1; } } return orderInd; } private final void clear(char[] c, int beginIndex, int endIndex) { for(int i=beginIndex; i < endIndex; i++) { if(c[i]!='\r' && c[i]!='\n' && c[i]!='\t') { c[i] = ' '; } } } private final void initPathNode(ASTPath pathNode, String path, int type) { pathNode.pathList = idManager.getPathList(path); pathNode.fieldList = idManager.getFieldList(path); pathNode.type = type; } private final void initParameterNode( ASTParameter parameterNode, String number, int type) { parameterNode.number = Integer.parseInt(number); parameterNode.type = type; } } PARSER_END(JBossQLParser) ////////////////////// New or changed elements ///////////////////////////// ASTEJBQL EJBQL() #EJBQL : {} { SelectClause() FromKeyword() [WhereClause()] [OrderByClause()] [LimitClause()] { return jjtThis; } } ASTOrderBy OrderByClause() #OrderBy : {} { OrderByPathExpression() ( OrderByPathExpression() ) * { return jjtThis; } } void OrderByPathExpression() #OrderByPath : {} { ( NumericValuedPath() | StringValuedPath() | DatetimeValuedPath() | ValueClassExpression()) [ | { jjtThis.ascending=false; } ] } void LimitClause() #LimitOffset : {} { ArithmeticValue() { jjtThis.hasOffset = true; } [ ArithmeticValue() { jjtThis.hasLimit = true; }] | ArithmeticValue() { jjtThis.hasLimit = true; } } //////////////////////////////////////////////////////////////////////////// void FromKeyword() #From : {} { } ASTFrom ParseFromClause() #void : { ASTFrom node; } { node = FromClause() { return node; } } ASTFrom FromClause() #From : {} { ( IdentificationVariableDeclaration() ( IdentificationVariableDeclaration() )* ) { return jjtThis; } } void IdentificationVariableDeclaration() #void : {} { (CollectionMemberDeclaration() | RangeVariableDeclaration()) } void CollectionMemberDeclaration() : { Token path; Token id; } { ( path=CollectionValuedPath() [] id=Identifier() ) { idManager.declareCollectionMember(id.image, path.image); } } void RangeVariableDeclaration() : { Token schema; Token id; } { ( schema=AbstractSchema() [] id=Identifier() ) { idManager.declareRangeVariable(id.image, schema.image); } } ASTSelect SelectClause() #Select : {} { (