/*******************************************************************************
* Copyright (c) 2007 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.el.internal.core.parser;
import java.util.List;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.el.core.parser.LexicalToken;
import org.jboss.tools.common.el.core.parser.Tokenizer;
import org.jboss.tools.common.el.internal.core.model.ELArgumentImpl;
import org.jboss.tools.common.el.internal.core.model.ELArgumentExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELArrayImpl;
import org.jboss.tools.common.el.internal.core.model.ELComplexExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELComplexInvocationExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELInstanceImpl;
import org.jboss.tools.common.el.internal.core.model.ELInvocationExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELMethodInvocationImpl;
import org.jboss.tools.common.el.internal.core.model.ELModelImpl;
import org.jboss.tools.common.el.internal.core.model.ELMultiExpressionImpl;
import org.jboss.tools.common.el.internal.core.model.ELOperatorImpl;
import org.jboss.tools.common.el.internal.core.model.ELParametersImpl;
import org.jboss.tools.common.el.internal.core.model.ELPropertyInvocationImpl;
import org.jboss.tools.common.el.internal.core.model.ELValueExpressionImpl;
import org.jboss.tools.common.el.internal.core.parser.token.ArgEndTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ArgStartTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ArrayEndTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ArrayStartTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.CommaTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.DotTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.EndELTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ExprEndTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ExprStartTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.JavaNameTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.OperationTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ParamEndTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.ParamStartTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.PrimitiveValueTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.StartELTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.StringTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.UnaryTokenDescription;
import org.jboss.tools.common.el.internal.core.parser.token.WhiteSpaceTokenDescription;
/**
*
* @author V. Kabanovich
*
*/
public class ELParserImpl {
LexicalToken current;
public ELModelImpl parse(LexicalToken start) {
if(current != null) {
throw new RuntimeException("Cannot reuse parser while it is running."); //$NON-NLS-1$
}
try {
ELModelImpl model = new ELModelImpl();
model.setFirstToken(start);
current = start;
while (current != null) {
if (current.getType() == StartELTokenDescription.START_EL) {
ELInstanceImpl instance = readELInstance();
if (instance != null) {
model.addInstance(instance);
}
} else if (!hasNextToken()) {
break;
} else {
if (lookUpNextToken(current) == null) {
break;
}
setNextToken();
}
}
return model;
} finally {
current = null;
}
}
protected ELInstanceImpl readELInstance() {
if(current == null || current.getType() != StartELTokenDescription.START_EL) {
return null;
}
ELInstanceImpl instance = new ELInstanceImpl();
instance.setFirstToken(current);
ELExpressionImpl expression = null;
// setNextToken();
if(lookUpNextToken(current) != null) {
setNextToken();
expression = readExpression();
} else {
current = current.getNextToken();
}
if(expression == null) {
//create fake invocation expression
expression = new ELPropertyInvocationImpl();
int p = current != null ? current.getStart() : instance.getEndPosition();
LexicalToken t = new LexicalToken(p, 0, "", JavaNameTokenDescription.JAVA_NAME); //$NON-NLS-1$
expression.setFirstToken(t);
expression.setLastToken(t);
}
if(expression != null) {
instance.setExpression(expression);
instance.setLastToken(expression.getLastToken());
}
do {
if(current == null) {
if(instance.getLastToken() == null) {
instance.setLastToken(instance.getFirstToken());
}
return instance;
} else if(current.getType() == StartELTokenDescription.START_EL) {
instance.setLastToken(current.getPreviousToken());
return instance;
} else if(current.getType() == EndELTokenDescription.END_EL) {
instance.setLastToken(current);
setNextToken();
return instance;
} else if(!hasNextToken()) {
instance.setLastToken(current);
return instance;
}
setNextToken();
} while(true);
}
protected ELExpressionImpl readExpression() {
if(current == null) return null;
ELExpressionImpl single = readSingleExpression();
if(single == null) return null;
if(current == null || current.getType() != OperationTokenDescription.OPERATION) {
return single;
}
ELMultiExpressionImpl multi = new ELMultiExpressionImpl();
multi.setFirstToken(single.getFirstToken());
multi.addExpression(single);
while(current != null && current.getType() == OperationTokenDescription.OPERATION && hasNextToken()) {
ELOperatorImpl operator = new ELOperatorImpl();
operator.setFirstToken(current);
operator.setLastToken(current);
multi.addOperator(operator);
multi.setLastToken(operator.getLastToken());
setNextToken();
single = readSingleExpression();
if(single != null) {
multi.addExpression(single);
multi.setLastToken(single.getLastToken());
}
}
return multi;
}
protected ELExpressionImpl readSingleExpression() {
if(current == null) return null;
switch(current.getType()) {
case ExprStartTokenDescription.EXPR_START:
ELComplexExpressionImpl complex = readComplexExpression();
if(current != null && current.getType() == DotTokenDescription.DOT) {
return readComplexInvocationExpression(complex);
} else {
return complex;
}
case UnaryTokenDescription.UNARY:
return readComplexExpression();
case StringTokenDescription.STRING:
LexicalToken f = lookUpNextToken(current);
if(f != null && f.getType() == DotTokenDescription.DOT) {
return readInvocationExpression();
}
case PrimitiveValueTokenDescription.PRIMITIVE_VALUE:
ELExpressionImpl expr = new ELValueExpressionImpl();
expr.setFirstToken(current);
expr.setLastToken(current);
setNextToken();
return expr;
case JavaNameTokenDescription.JAVA_NAME:
LexicalToken t = lookUpNextToken(current);
if(t != null && t.getType() == OperationTokenDescription.OPERATION && t.getText().equals(":")) { //$NON-NLS-1$
LexicalToken t1 = lookUpNextToken(t);
if(t1 == null || t1.getType() == EndELTokenDescription.END_EL || t1.getType() == ParamEndTokenDescription.PARAM_END
|| t1.getType() == ArgEndTokenDescription.ARG_END || t1.getType() == ExprEndTokenDescription.EXPR_END
|| t1.getType() == CommaTokenDescription.COMMA
|| t1.getType() == OperationTokenDescription.OPERATION || t1.getType() == StartELTokenDescription.START_EL) {
t.setType(DotTokenDescription.DOT); //in incomplete expressions prefer function call to operation sign
} else if(t1 != null && t1.getType() == JavaNameTokenDescription.JAVA_NAME) {
LexicalToken t2 = lookUpNextToken(t1);
if(t2 != null && t2.getType() == ParamStartTokenDescription.PARAM_START) {
t.setType(DotTokenDescription.DOT);
} else {
LexicalToken t_ = lookUpPrevToken(current);
if(t_ == null || !"?".equals(t_.getText())) { //$NON-NLS-1$
t.setType(DotTokenDescription.DOT);
}
}
}
}
return readInvocationExpression();
case ArrayStartTokenDescription.ARRAY_START:
return readArray();
}
return null;
}
protected ELComplexExpressionImpl readComplexExpression() {
ELComplexExpressionImpl expr = new ELComplexExpressionImpl();
expr.setFirstToken(current);
if(!hasNextToken()) {
expr.setLastToken(current);
return expr;
}
setNextToken();
ELExpressionImpl child = readExpression();
if(child != null) {
expr.setExpression(child);
expr.setLastToken(child.getLastToken());
}
if(current != null && current.getType() == ExprEndTokenDescription.EXPR_END) {
expr.setLastToken(current);
setNextToken();
}
return expr;
}
protected ELComplexInvocationExpressionImpl readComplexInvocationExpression(ELComplexExpressionImpl complex) {
ELInvocationExpressionImpl left = null;
List<ELInvocationExpression> is = complex.getInvocations();
if(is != null && !is.isEmpty()) {
left = (ELInvocationExpressionImpl)is.get(0);
} else {
ELPropertyInvocationImpl fake = new ELPropertyInvocationImpl();
LexicalToken t = new LexicalToken(current.getStart(), 0, "", StringTokenDescription.STRING); //$NON-NLS-1$
fake.setName(t);
left = fake;
}
ELComplexInvocationExpressionImpl result = new ELComplexInvocationExpressionImpl();
result.setExpression(complex);
ELInvocationExpressionImpl right = readRightExpression(left, true);
if(right != left) {
result.setInvocation(right);
}
result.setFirstToken(complex.getFirstToken());
result.setLastToken(right.getLastToken());
return result;
}
protected ELInvocationExpressionImpl readInvocationExpression() {
if(current == null ||
(current.getType() != JavaNameTokenDescription.JAVA_NAME &&
current.getType() != StringTokenDescription.STRING)) {
return null;
}
ELPropertyInvocationImpl name = new ELPropertyInvocationImpl();
name.setName(current);
ELInvocationExpressionImpl result = name;
setNextToken();
if(current != null) switch (current.getType()) {
case ParamStartTokenDescription.PARAM_START:
ELParametersImpl params = readParameters();
ELMethodInvocationImpl method = new ELMethodInvocationImpl();
method.setName(name.getName());
method.setParameters(params);
result = method;
//do not break we are ready to have [] suffix here
case ArgStartTokenDescription.ARG_START:
while(current != null && current.getType() == ArgStartTokenDescription.ARG_START) {
ELArgumentImpl arg = readArgument();
ELArgumentExpressionImpl call = new ELArgumentExpressionImpl();
call.setArgument(arg);
call.setLeft(result);
result = call;
}
break;
}
return readRightExpression(result, false);
}
ELInvocationExpressionImpl readRightExpression(ELInvocationExpressionImpl result, boolean isLeftFake) {
if(current != null && current.getType() == DotTokenDescription.DOT) {
LexicalToken dot = current;
setNextToken();
ELInvocationExpressionImpl right = readInvocationExpression();
if(right != null) {
ELInvocationExpressionImpl r = right;
while(r.getLeft() != null) r = r.getLeft();
if(r instanceof ELPropertyInvocationImpl) {
((ELPropertyInvocationImpl)r).setSeparator(dot);
} else {
//is it possible?
}
r.setLeft(result);
r.setLeftIsFake(isLeftFake);
result = right;
} else {
ELPropertyInvocationImpl incompleteProperty = new ELPropertyInvocationImpl();
incompleteProperty.setSeparator(dot);
incompleteProperty.setLastToken(dot);
LexicalToken n = dot.getNextToken();
if(n != null && n.getType() == WhiteSpaceTokenDescription.WHITESPACE) {
incompleteProperty.setLastToken(n);
}
incompleteProperty.setLeft(result);
incompleteProperty.setLeftIsFake(isLeftFake);
result = incompleteProperty;
}
}
return result;
}
protected ELParametersImpl readParameters() {
ELParametersImpl parameters = new ELParametersImpl();
parameters.setFirstToken(current);
if(!hasNextToken()) {
parameters.setLastToken(current);
return parameters;
}
setNextToken();
ELExpressionImpl expression = readExpression();
if(expression != null) {
parameters.addParameter(expression);
parameters.setLastToken(expression.getLastToken());
}
while(current != null && current.getType() == CommaTokenDescription.COMMA) {
if(!hasNextToken()) {
parameters.setLastToken(current);
return parameters;
}
setNextToken();
expression = readExpression();
if(expression != null) {
parameters.addParameter(expression);
parameters.setLastToken(expression.getLastToken());
}
}
if(current != null && current.getType() == ParamEndTokenDescription.PARAM_END) {
parameters.setLastToken(current);
setNextToken();
}
return parameters;
}
protected ELArrayImpl readArray() {
ELArrayImpl array = new ELArrayImpl();
array.setFirstToken(current);
if(!hasNextToken()) {
array.setLastToken(current);
return array;
}
setNextToken();
ELExpressionImpl expression = readExpression();
if(expression != null) {
array.addValue(expression);
array.setLastToken(expression.getLastToken());
}
while(current != null && current.getType() == CommaTokenDescription.COMMA) {
if(!hasNextToken()) {
array.setLastToken(current);
return array;
}
setNextToken();
expression = readExpression();
if(expression != null) {
array.addValue(expression);
array.setLastToken(expression.getLastToken());
}
}
if(current != null && current.getType() == ArrayEndTokenDescription.ARRAY_END) {
array.setLastToken(current);
setNextToken();
}
return array;
}
protected ELArgumentImpl readArgument() {
ELArgumentImpl arg = new ELArgumentImpl();
arg.setFirstToken(current);
arg.setLastToken(current);
if(!hasNextToken()) {
setNextToken();
return arg;
}
setNextToken();
ELExpressionImpl expr = readExpression();
if(expr != null) {
arg.setArgument(expr);
arg.setLastToken(expr.getLastToken());
}
if(current != null && current.getType() == ArgEndTokenDescription.ARG_END) {
arg.setLastToken(current);
setNextToken();
}
return arg;
}
private boolean hasNextToken() {
return lookUpNextToken(current) != null;
}
private LexicalToken lookUpNextToken(LexicalToken token) {
LexicalToken c = token;
while(c != null
&& (c == token
|| c.getType() == WhiteSpaceTokenDescription.WHITESPACE
|| c.getType() == Tokenizer.LITERAL)) {
c = c.getNextToken();
}
return c;
}
private LexicalToken lookUpPrevToken(LexicalToken token) {
LexicalToken c = token;
while(c != null
&& (c == token
|| c.getType() == WhiteSpaceTokenDescription.WHITESPACE
|| c.getType() == Tokenizer.LITERAL)) {
c = c.getPreviousToken();
}
return c;
}
private void setNextToken() {
current = lookUpNextToken(current);
}
}