org.richfaces.cdk.templatecompiler.el.ELVisitor Maven / Gradle / Ivy
The newest version!
/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.richfaces.cdk.templatecompiler.el;
import static org.richfaces.cdk.templatecompiler.statements.HelperMethod.TO_STRING_CONVERSION;
import static org.richfaces.cdk.util.JavaUtils.getEscapedString;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import org.jboss.el.parser.AstAnd;
import org.jboss.el.parser.AstBracketSuffix;
import org.jboss.el.parser.AstChoice;
import org.jboss.el.parser.AstCompositeExpression;
import org.jboss.el.parser.AstDeferredExpression;
import org.jboss.el.parser.AstDiv;
import org.jboss.el.parser.AstDynamicExpression;
import org.jboss.el.parser.AstEmpty;
import org.jboss.el.parser.AstEqual;
import org.jboss.el.parser.AstFalse;
import org.jboss.el.parser.AstFloatingPoint;
import org.jboss.el.parser.AstFunction;
import org.jboss.el.parser.AstGreaterThan;
import org.jboss.el.parser.AstGreaterThanEqual;
import org.jboss.el.parser.AstIdentifier;
import org.jboss.el.parser.AstInteger;
import org.jboss.el.parser.AstLessThan;
import org.jboss.el.parser.AstLessThanEqual;
import org.jboss.el.parser.AstLiteralExpression;
import org.jboss.el.parser.AstMethodSuffix;
import org.jboss.el.parser.AstMinus;
import org.jboss.el.parser.AstMod;
import org.jboss.el.parser.AstMult;
import org.jboss.el.parser.AstNegative;
import org.jboss.el.parser.AstNot;
import org.jboss.el.parser.AstNotEqual;
import org.jboss.el.parser.AstNull;
import org.jboss.el.parser.AstOr;
import org.jboss.el.parser.AstPlus;
import org.jboss.el.parser.AstPropertySuffix;
import org.jboss.el.parser.AstString;
import org.jboss.el.parser.AstTrue;
import org.jboss.el.parser.AstValue;
import org.jboss.el.parser.ELParser;
import org.jboss.el.parser.Node;
import org.richfaces.cdk.Logger;
import org.richfaces.cdk.templatecompiler.builder.model.JavaField;
import org.richfaces.cdk.templatecompiler.builder.model.JavaImport;
import org.richfaces.cdk.templatecompiler.builder.model.Variables;
import org.richfaces.cdk.templatecompiler.el.node.AstBracketSuffixTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstChoiceTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstCompositeComponentAttributesTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstCompositeComponentTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstDeferredOrDynamicExpressionTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstEmptyTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstFloatingPointTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstFunctionTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstIdentifierTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstIntegerTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstLiteralTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstMethodSuffixTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstNegativeTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstNotTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstPropertySuffixTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstStringTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.AstValueTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.BinaryArithmeticIntegerOperationTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.BinaryArithmeticOperationTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.BinaryBooleanOperationTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.BinaryBooleanResultOperationTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.ConstantValueTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.EqualityTestTreeNode;
import org.richfaces.cdk.templatecompiler.el.node.ITreeNode;
import org.richfaces.cdk.templatecompiler.el.types.ELPropertyDescriptor;
import org.richfaces.cdk.templatecompiler.el.types.ELType;
import org.richfaces.cdk.templatecompiler.el.types.TypesFactory;
import org.richfaces.cdk.templatecompiler.statements.HelperMethod;
import org.richfaces.cdk.templatecompiler.statements.StatementsContainer;
import org.richfaces.cdk.templatecompiler.statements.TypedTemplateStatement;
/**
* Entry point for parsing EL expressions. @see parse() method.
*
* @author amarkhel
*/
public final class ELVisitor implements TypedTemplateStatement {
private String parsedExpression = null;
private ELType expressionType = null;
private Variables variables = null;
private Set usedHelperMethods = EnumSet.noneOf(HelperMethod.class);
private final Logger log;
private final TypesFactory typesFactory;
private boolean mixedExpression;
private boolean literal = true;
private StatementsContainer parent;
public ELVisitor(Logger log, TypesFactory typesFactory) {
this.log = log;
this.typesFactory = typesFactory;
}
public boolean isMixedExpression() {
return mixedExpression;
}
/**
*
*
*
* @return the literal
*/
@Override
public boolean isLiteral() {
return this.literal;
}
/**
*
*
*
* @param literal the literal to set
*/
public void setLiteral(boolean literal) {
this.literal = literal;
}
public ELType getType() {
return expressionType;
}
public void setExpressionType(ELType variableType) {
this.expressionType = variableType;
}
public ELType getVariable(String name) throws ParsingException {
ELType variableType;
if (variables.isDefined(name)) {
variableType = variables.getVariable(name);
} else {
log.warn(MessageFormat.format("No type found in context for identifier ''{0}'', handling as generic Object", name));
variableType = TypesFactory.OBJECT_TYPE;
}
return variableType;
}
/**
* Parse specified EL expression and return Java code, that represent this expression
*
* @param expression - expression to resolve
* @param contextVariables - Map<String, Class<?>> - context for search classes.
*
* @throws ParsingException - if error occurred during parsing.
*/
public void parse(String expression, Variables contextVariables, ELType expectedType) throws ParsingException {
reset();
Node ret = ELParser.parse(expression);
variables = contextVariables;
if (ret instanceof AstCompositeExpression && ret.jjtGetNumChildren() >= 2) {
// AstCompositeExpression with 2+ children is a mixed expression
usedHelperMethods.add(TO_STRING_CONVERSION);
this.mixedExpression = true;
this.literal = false;
}
if (ret != null && ret.jjtGetNumChildren() > 0) {
parsedExpression = this.visit(ret);
} else {
parsedExpression = getEscapedString("");
expressionType = TypesFactory.STRING_TYPE;
}
parsedExpression = coerceToType(parsedExpression, expectedType);
}
public String coerceToType(String valueString, ELType expectedType) {
if (!expectedType.isAssignableFrom(getType())) {
for (HelperMethod conversionMethod : HelperMethod.getConversionMethods()) {
ELType returnType = typesFactory.getType(conversionMethod.getReturnType());
if (expectedType.isAssignableFrom(returnType)) {
usedHelperMethods.add(conversionMethod);
setExpressionType(returnType);
return conversionMethod.getName() + "(" + valueString + ")";
}
}
setLiteral(false);
}
return valueString;
}
private String visit(Node node) throws ParsingException {
int numChildren = node.jjtGetNumChildren();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < numChildren; i++) {
Node child = node.jjtGetChild(i);
ITreeNode treeNode = determineNodeType(child);
treeNode.visit(sb, this);
if (i != numChildren - 1) {
sb.append(" + ");
}
}
return sb.toString();
}
private void reset() {
parsedExpression = null;
usedHelperMethods.clear();
variables = null;
expressionType = null;
}
@Override
public String getCode() {
return parsedExpression;
}
@Override
public Iterable getRequiredImports() {
return getType().getRequiredImports();
}
@Override
public Iterable getRequiredFields() {
return Collections.emptySet();
}
@Override
public Iterable getRequiredMethods() {
return usedHelperMethods;
}
public ELType getMatchingVisibleMethodReturnType(String methodName, ELType[] parameterTypes) throws ParsingException {
return typesFactory.getMatchingVisibleMethodReturnType(getType(), methodName, parameterTypes);
}
public ELPropertyDescriptor getPropertyDescriptor(String propertyName) throws ParsingException {
return typesFactory.getPropertyDescriptor(getType(), propertyName);
}
public void addHelperMethods(HelperMethod helper) {
usedHelperMethods.add(helper);
}
/**
* This method determine type of parsed node and create wrapper for them, that extends AbstractTreeNode. If node type is not
* recognized - throws ParsingException.
*
* @param child - parsed node
* @throws ParsingException - if node type is not recognized.
* @return wrapper for parsed node(if node type is recognized), that implement ITreeNode interface.
*/
public ITreeNode determineNodeType(Node child) throws ParsingException {
ITreeNode treeNode = null;
if (child instanceof AstIdentifier) {
if (isCompositeComponent(child)) {
treeNode = new AstCompositeComponentTreeNode(child);
} else {
treeNode = new AstIdentifierTreeNode(child);
}
} else if (child instanceof AstValue) {
treeNode = new AstValueTreeNode(child);
} else if (child instanceof AstInteger) {
treeNode = new AstIntegerTreeNode(child);
} else if (child instanceof AstString) {
treeNode = new AstStringTreeNode(child);
} else if (child instanceof AstFunction) {
treeNode = new AstFunctionTreeNode(child);
} else if (child instanceof AstDeferredExpression || child instanceof AstDynamicExpression) {
treeNode = new AstDeferredOrDynamicExpressionTreeNode(child);
} else if (child instanceof AstNot) {
treeNode = new AstNotTreeNode(child);
} else if (child instanceof AstChoice) {
treeNode = new AstChoiceTreeNode(child);
} else if (child instanceof AstEmpty) {
treeNode = new AstEmptyTreeNode(child);
} else if (child instanceof AstLiteralExpression) {
treeNode = new AstLiteralTreeNode(child);
} else if (child instanceof AstFalse) {
treeNode = ConstantValueTreeNode.FALSE_NODE;
} else if (child instanceof AstTrue) {
treeNode = ConstantValueTreeNode.TRUE_NODE;
} else if (child instanceof AstNull) {
treeNode = ConstantValueTreeNode.NULL_NODE;
} else if (child instanceof AstAnd) {
treeNode = new BinaryBooleanOperationTreeNode(child, ELNodeConstants.AND_OPERATOR);
} else if (child instanceof AstEqual) {
treeNode = new EqualityTestTreeNode(child);
} else if (child instanceof AstGreaterThan) {
treeNode = new BinaryBooleanResultOperationTreeNode(child, ELNodeConstants.GREATER_THEN_OPERATOR);
} else if (child instanceof AstGreaterThanEqual) {
treeNode = new BinaryBooleanResultOperationTreeNode(child, ELNodeConstants.GREATER_THEN_OR_EQUALITY_OPERATOR);
} else if (child instanceof AstLessThan) {
treeNode = new BinaryBooleanResultOperationTreeNode(child, ELNodeConstants.LESS_THEN_OPERATOR);
} else if (child instanceof AstLessThanEqual) {
treeNode = new BinaryBooleanResultOperationTreeNode(child, ELNodeConstants.LESS_THEN_OR_EQUALITY_OPERATOR);
} else if (child instanceof AstNotEqual) {
treeNode = new EqualityTestTreeNode(child, true);
} else if (child instanceof AstOr) {
treeNode = new BinaryBooleanOperationTreeNode(child, ELNodeConstants.OR_OPERATOR);
} else if (child instanceof AstDiv) {
treeNode = new BinaryArithmeticOperationTreeNode(child, ELNodeConstants.DIV_OPERATOR);
} else if (child instanceof AstMult) {
treeNode = new BinaryArithmeticOperationTreeNode(child, ELNodeConstants.MULT_OPERATOR);
} else if (child instanceof AstMod) {
treeNode = new BinaryArithmeticIntegerOperationTreeNode(child, ELNodeConstants.MOD_OPERATOR);
} else if (child instanceof AstPlus) {
treeNode = new BinaryArithmeticOperationTreeNode(child, ELNodeConstants.PLUS_OPERATOR);
} else if (child instanceof AstMinus) {
treeNode = new BinaryArithmeticOperationTreeNode(child, ELNodeConstants.MINUS_OPERATOR);
} else if (child instanceof AstBracketSuffix) {
treeNode = new AstBracketSuffixTreeNode(child);
} else if (child instanceof AstNegative) {
treeNode = new AstNegativeTreeNode(child);
} else if (child instanceof AstFloatingPoint) {
treeNode = new AstFloatingPointTreeNode(child);
} else if (child instanceof AstMethodSuffix) {
treeNode = new AstMethodSuffixTreeNode(child);
} else if (child instanceof AstPropertySuffix) {
if (isCompositeComponentAttributesMap(child)) {
treeNode = new AstCompositeComponentAttributesTreeNode(child);
} else if (isCompositeComponentAttribute(child)) {
treeNode = new AstIdentifierTreeNode(child);
} else {
treeNode = new AstPropertySuffixTreeNode(child);
}
} else {
throw new ParsingException("Node " + child.getClass().getSimpleName() + "[" + child.getImage()
+ "] is not recognized;");
}
return treeNode;
}
private boolean isCompositeComponent(Node node) {
return "cc".equals(node.getImage());
}
private boolean isCompositeComponentAttributesMap(Node node) {
return "attrs".equals(node.getImage()) && isCompositeComponent(node.jjtGetParent().jjtGetChild(0));
}
private boolean isCompositeComponentAttribute(Node node) {
return isCompositeComponentAttributesMap(node.jjtGetParent().jjtGetChild(1))
&& node == node.jjtGetParent().jjtGetChild(2);
}
@Override
public void setParent(StatementsContainer parent) {
this.parent = parent;
}
}