/**
* 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.ajax4jsf.templatecompiler.el;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.el.PropertyNotFoundException;
import org.ajax4jsf.templatecompiler.builder.CompilationContext;
import org.ajax4jsf.templatecompiler.builder.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.sun.el.parser.ArithmeticNode;
import com.sun.el.parser.AstAnd;
import com.sun.el.parser.AstBracketSuffix;
import com.sun.el.parser.AstChoice;
import com.sun.el.parser.AstDeferredExpression;
import com.sun.el.parser.AstDiv;
import com.sun.el.parser.AstDotSuffix;
import com.sun.el.parser.AstEmpty;
import com.sun.el.parser.AstEqual;
import com.sun.el.parser.AstFalse;
import com.sun.el.parser.AstFunction;
import com.sun.el.parser.AstGreaterThan;
import com.sun.el.parser.AstGreaterThanEqual;
import com.sun.el.parser.AstIdentifier;
import com.sun.el.parser.AstInteger;
import com.sun.el.parser.AstLessThan;
import com.sun.el.parser.AstLessThanEqual;
import com.sun.el.parser.AstLiteralExpression;
import com.sun.el.parser.AstMinus;
import com.sun.el.parser.AstMod;
import com.sun.el.parser.AstMult;
import com.sun.el.parser.AstNot;
import com.sun.el.parser.AstNotEqual;
import com.sun.el.parser.AstOr;
import com.sun.el.parser.AstPlus;
import com.sun.el.parser.AstString;
import com.sun.el.parser.AstTrue;
import com.sun.el.parser.AstValue;
import com.sun.el.parser.BooleanNode;
import com.sun.el.parser.ELParser;
import com.sun.el.parser.Node;
/**
* Compiler EL-expressions.
*
* @author ayukhovich@exadel.com (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.2 $ $Date: 2007/02/21 17:17:12 $
*
*/
public class ELCompiler implements IELCompiler {
private static final Log log = LogFactory.getLog(ELCompiler.class);
Map<String, String> functionsMap = new HashMap<String,String>();
static {
}
/**
*
*/
public ELCompiler() {
super();
resetVariables();
}
/**
*
*/
public void resetVariables() {
// maps.put("context", "javax.faces.context.FacesContext" );
// maps.put("component", "javax.faces.component.UIComponent" );
// maps.put("a4jSkin", "org.ajax4jsf.framework.skin.Skin" );
this.functionsMap.put("a4jSkin:getParameter",
"org.ajax4jsf.framework.skin.getParameter");
}
public String compileEL(String expression, CompilationContext componentBean) {
Node node = ELParser.parse(expression);
StringBuffer sbMain = new StringBuffer();
processNode(node, sbMain, componentBean);
return sbMain.toString();
}
/**
* Processing node
*
* @param node
* @param sbMain
* @param componentBean
*/
private void processNode(Node node, StringBuffer sbMain,
CompilationContext componentBean) {
int numChildren = node.jjtGetNumChildren();
boolean bNeedConversion = false;
for (int i = 0; i < numChildren; i++) {
Node childNode = node.jjtGetChild(i);
if (childNode instanceof AstLiteralExpression) {
bNeedConversion = true;
break;
}
}
for (int i = 0; i < numChildren; i++) {
Node childNode = node.jjtGetChild(i);
if (childNode instanceof AstLiteralExpression) {
if (childNode.getImage() != null) {
if (i > 0) {
sbMain.append(" + ");
}
sbMain.append("\"");
sbMain.append(StringUtils.getEscapedString(childNode
.getImage()));
sbMain.append("\"");
if (i < (numChildren - 1)) {
sbMain.append(" + ");
}
}
} else {
if (bNeedConversion) {
sbMain.append("convertToString(");
}
boolean processing = processingNode(childNode, sbMain,
componentBean);
if (!processing) {
processNode(childNode, sbMain, componentBean);
}
if (bNeedConversion) {
sbMain.append(")");
}
}
}
}
/**
*
* @param node
* @param sbMain
* @param componentBean
* @param cMain
* @return
*/
public boolean processingNode(Node node, StringBuffer sbMain,
CompilationContext componentBean) {
boolean returnValue = false;
if (node instanceof ArithmeticNode) {
returnValue = processingArithmeticNode((ArithmeticNode) node,
sbMain, componentBean);
} else if (node instanceof AstIdentifier) {
returnValue = processingIdentifier((AstIdentifier) node, sbMain,
componentBean);
} else if (node instanceof AstValue) {
returnValue = processingValue((AstValue) node, sbMain,
componentBean);
} else if (node instanceof AstInteger) {
returnValue = processingInteger((AstInteger) node, sbMain);
} else if (node instanceof AstString) {
returnValue = processingString((AstString) node, sbMain);
} else if (node instanceof AstFunction) {
returnValue = processingFunction((AstFunction) node, sbMain,
componentBean);
} else if (node instanceof AstDeferredExpression) {
} else if (node instanceof BooleanNode) {
returnValue = processingBooleanNode((BooleanNode) node,
sbMain, componentBean);
} else if (node instanceof AstNot) {
returnValue = processingNot((AstNot) node,
sbMain, componentBean);
} else if (node instanceof AstChoice) {
returnValue = processingChoice((AstChoice) node,
sbMain, componentBean);
} else if (node instanceof AstEmpty) {
returnValue = processingEmpty((AstEmpty) node,
sbMain, componentBean);
} else {
StringBuffer sb = new StringBuffer();
sb.append(node.toString());
sb.append(" (");
sb.append(node.getClass().getName());
sb.append(")");
log.debug(sb.toString());
}
return returnValue;
}
/**
* Processing 'empty' node
*
* @param node
* @param sb
* @param cMain
* @return
*/
private boolean processingEmpty(AstEmpty node, StringBuffer sbMain,
CompilationContext componentBean) {
boolean returnValue = false;
StringBuffer sb1 = new StringBuffer();
Node node1 = node.jjtGetChild(0);
if (null != node1) {
if (!(returnValue = processingNode(node1, sb1,
componentBean))) {
log.error("Error processing node1: "
+ node1.getImage());
}
}
if (returnValue) {
sbMain.append(" getUtils().isEmpty( ");
sbMain.append(sb1);
sbMain.append(" ) ");
}
return returnValue;
}
/**
* Processing 'choice' node
*
* @param node
* @param sb
* @param cMain
* @return
*/
private boolean processingChoice(AstChoice node, StringBuffer sbMain,
CompilationContext componentBean) {
boolean returnValue = true;
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
StringBuffer sb3 = new StringBuffer();
Node node1 = node.jjtGetChild(0);
if (node1 != null) {
if (!(returnValue &= processingNode(node1, sb1,
componentBean))) {
log.error("Error processing node1: "
+ node1.getImage());
}
}
Node node2 = node.jjtGetChild(1);
if (null != node2) {
if (!(returnValue &= processingNode(node2, sb2,
componentBean))) {
log.error("Error processing node2: "
+ node2.getImage());
}
}
Node node3 = node.jjtGetChild(2);
if (null != node3) {
if (!(returnValue &= processingNode(node3, sb3,
componentBean))) {
log.error("Error processing node3: "
+ node3.getImage());
}
}
if (returnValue) {
sbMain.append(" ( ");
sbMain.append(sb1);
sbMain.append(" ? ");
sbMain.append(sb2);
sbMain.append(" : ");
sbMain.append(sb3);
sbMain.append(" ) ");
}
return returnValue;
}
/**
* Processing node containing 'not' expression
*
* @param node
* @param sb
* @param cMain
* @return
*/
private boolean processingNot(AstNot node, StringBuffer sbMain,
CompilationContext componentBean) {
boolean returnValue = false;
StringBuffer sb1 = new StringBuffer();
Node node1 = node.jjtGetChild(0);
if (null != node1) {
if (!(returnValue = processingNode(node1, sb1,
componentBean))) {
log.error("Error processing node1: "
+ node1.getImage());
}
}
if (returnValue) {
sbMain.append(" ( ! ");
sbMain.append(sb1);
sbMain.append(" ) ");
}
return returnValue;
}
/**
* Processing boolean node
*
* @param node
* @param sb
* @param cMain
* @return
*/
private boolean processingBooleanNode(BooleanNode node,
StringBuffer sb, CompilationContext componentBean) {
boolean returnValue = true;
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
if (node instanceof AstFalse) {
sb.append(" false ");
return returnValue;
}
if (node instanceof AstTrue) {
sb.append(" true ");
return returnValue;
}
Node node1 = node.jjtGetChild(0);
if (node1 != null) {
if (!(returnValue &= processingNode(node1, sb1,
componentBean))) {
log.error("Error processing node1: "
+ node1.getImage());
}
}
Node node2 = node.jjtGetChild(1);
if (null != node2) {
if (!(returnValue &= processingNode(node2, sb2,
componentBean))) {
log.error("Error processing node2: "
+ node2.getImage());
}
}
if (returnValue) {
sb.append(" ( ");
sb.append(sb1);
if (node instanceof AstAnd) {
sb.append(" && ");
} else if (node instanceof AstEqual) {
sb.append(" == ");
} else if (node instanceof AstGreaterThan) {
sb.append(" > ");
} else if (node instanceof AstGreaterThanEqual) {
sb.append(" >= ");
} else if (node instanceof AstLessThan) {
sb.append(" < ");
} else if (node instanceof AstLessThanEqual) {
sb.append(" <= ");
} else if (node instanceof AstNotEqual) {
sb.append(" != ");
} else if (node instanceof AstOr) {
sb.append(" || ");
}
sb.append(sb2);
sb.append(" ) ");
}
return returnValue;
}
/**
* Processing arithmetic node
*
* @param node
* @param sb
* @param cMain
* @return
*/
private boolean processingArithmeticNode(ArithmeticNode node,
StringBuffer sb, CompilationContext componentBean) {
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
boolean returnValue = true;
Node node1 = node.jjtGetChild(0);
if (node1 != null) {
if (!(returnValue &= processingNode(node1, sb1,
componentBean))) {
log.error("Error processing node1: "
+ node1.getImage());
}
}
Node node2 = node.jjtGetChild(1);
if (null != node2) {
if (!(returnValue &= processingNode(node2, sb2,
componentBean))) {
log.error("Error processing node2: "
+ node2.getImage());
}
}
if (returnValue) {
sb.append(" ( ");
sb.append(sb1);
if (node instanceof AstDiv) {
sb.append(" / ");
} else if (node instanceof AstMult) {
sb.append(" * ");
} else if (node instanceof AstMod) {
sb.append(" % ");
} else if (node instanceof AstPlus) {
sb.append(" + ");
} else if (node instanceof AstMinus) {
sb.append(" - ");
}
sb.append(sb2);
sb.append(" ) ");
}
return returnValue;
}
/**
* Processing node contain integer
*
* @param node
* @param sb
* @return
*/
private boolean processingInteger(AstInteger node, StringBuffer sb) {
sb.append(node.getImage());
return true;
}
/**
* Processing node contain string
*
* @param node
* @param sb
* @return
*/
private boolean processingString(AstString node, StringBuffer sb) {
sb.append("\"");
sb.append(node.getString());
sb.append("\"");
return true;
}
/**
*
* @param node
* @param sb
* @return
*/
private boolean processingFunction(AstFunction node, StringBuffer sb,
CompilationContext componentBean) {
log.debug("Processing function : " + node.getPrefix());
log.debug("Processing function : " + node.getLocalName());
log.debug("Processing function : " + node.getOutputName());
String prefix = node.getPrefix();
boolean isThis = prefix.equals("this");
boolean isUtils = prefix.equals("utils");
if (isThis || isUtils) {
if (isUtils) {
sb.append("getUtils().");
}
sb.append(node.getLocalName());
sb.append("(");
int numChildren = node.jjtGetNumChildren();
for (int i = 0; i < numChildren; i++) {
Node childNode = node.jjtGetChild(i);
StringBuffer buf = new StringBuffer();
processingNode(childNode, buf, componentBean);
if (i != 0) {
sb.append(",");
}
sb.append(buf);
}
sb.append(")");
} else {
String functionName = node.getOutputName();
if (this.functionsMap.containsKey(functionName)) {
sb.append(this.functionsMap.get(functionName));
sb.append("(");
int numChildren = node.jjtGetNumChildren();
for (int i = 0; i < numChildren; i++) {
Node childNode = node.jjtGetChild(i);
StringBuffer buf = new StringBuffer();
processingNode(childNode, buf, componentBean);
if (i != 0) {
sb.append(",");
}
sb.append(buf);
}
sb.append(")");
} // if
} // else
return true;
}
/**
*
* @param node
* @param sb
* @return
*/
private boolean processingIdentifier(AstIdentifier node, StringBuffer sb,
CompilationContext componentBean) {
String variableName = node.getImage();
if (componentBean.containsVariable(variableName)) {
sb.append(variableName);
} else {
sb.append("variables.getVariable(\"");
sb.append(variableName);
sb.append("\")");
}
return true;
}
/**
*
* @param basketSuffix
* @return
*/
private String processingBracketSuffix(AstBracketSuffix basketSuffix,
CompilationContext componentBean) {
StringBuffer sb = new StringBuffer();
Node node = basketSuffix.jjtGetChild(0);
if (node instanceof AstIdentifier) {
processingIdentifier((AstIdentifier) node, sb, componentBean);
} else if (node instanceof AstInteger) {
// sb.append("new Integer(");
sb.append(node.getImage());
// sb.append(")");
} else if (node instanceof AstString) {
AstString stringNode = (AstString) node;
sb.append("\"");
sb.append(stringNode.getString());
sb.append("\"");
} else {
sb.append("\"");
sb.append(node.getImage());
sb.append("\"");
}
return sb.toString();
}
/**
*
* @param clazz
* @param propertyName
* @return
*/
private PropertyDescriptor getPropertyDescriptor(Class<?> clazz,
String propertyName, CompilationContext compilationContext) {
return compilationContext.getPropertyDescriptor(clazz, propertyName);
}
private boolean processingValue(AstValue node, StringBuffer sb,
CompilationContext componentBean) {
String lastIndexValue = "null";
String lastVariableType = null;
List<String> names = new ArrayList<String>();
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
StringBuffer sb1 = new StringBuffer();
Node subChild = node.jjtGetChild(i);
if (subChild instanceof AstIdentifier) {
String variableName = subChild.getImage();
if (componentBean.containsVariable(variableName)) {
lastVariableType = componentBean.getVariableType(
variableName).getName();
names.add(variableName);
} else {
processingIdentifier((AstIdentifier) subChild, sb1,
componentBean);
}
} else if (subChild instanceof AstDotSuffix) {
String propertyName = subChild.getImage();
log.debug("Object: " + lastVariableType
+ ", property: " + propertyName);
if (lastVariableType != null) {
try {
Class<?> clazz = componentBean.loadClass(lastVariableType);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(
clazz, propertyName, componentBean);
if (propertyDescriptor == null) {
throw new PropertyNotFoundException("property: "
+ propertyName + " not found in class: "
+ lastVariableType);
}
log.debug("propertyObject: "
+ propertyDescriptor.getPropertyType()
.getName());
StringBuffer tmpbuf = new StringBuffer();
tmpbuf.append(propertyDescriptor.getReadMethod()
.getName());
tmpbuf.append("()");
names.add(tmpbuf.toString());
lastVariableType = propertyDescriptor.getPropertyType()
.getName();
} catch (ClassNotFoundException e) {
log.error(e.getLocalizedMessage(), e);
}
} else {
sb1.append("getProperty(");
sb1.append(lastIndexValue);
sb1.append(",");
sb1.append("\"");
sb1.append(subChild.getImage());
sb1.append("\")");
}
} else if (subChild instanceof AstBracketSuffix) {
String bracketSuffix = processingBracketSuffix(
(AstBracketSuffix) subChild, componentBean);
if (lastVariableType != null) {
StringBuffer tmpbuf = new StringBuffer();
if (lastVariableType.startsWith("[L")) {
tmpbuf.append("[");
tmpbuf.append(bracketSuffix);
tmpbuf.append("]");
names.add(tmpbuf.toString());
}
if ((lastVariableType.compareTo("java.util.List") == 0)
|| (lastVariableType.compareTo("java.util.Map") == 0)) {
tmpbuf.append("get(");
tmpbuf.append(bracketSuffix);
tmpbuf.append(")");
names.add(tmpbuf.toString());
}
} else {
sb1.append("getElelmentByIndex(");
sb1.append(lastIndexValue);
sb1.append(",");
sb1.append(bracketSuffix);
sb1.append(")");
}
}
}
if (names.size() != 0) {
StringBuffer tmpbuf = new StringBuffer();
for (String element : names) {
if (tmpbuf.length() != 0) {
tmpbuf.append(".");
}
tmpbuf.append(element);
}
sb.append(tmpbuf.toString());
} else {
sb.append(lastIndexValue);
}
return true;
}
}