/**
* Copyright (c) 2014, the Railo Company Ltd.
* Copyright (c) 2015, Lucee Assosication Switzerland
*
* This library 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 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, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.transformer.cfml.evaluator.impl;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import lucee.commons.lang.StringUtil;
import lucee.runtime.PageSource;
import lucee.runtime.functions.system.CFFunction;
import lucee.runtime.listener.AppListenerUtil;
import lucee.transformer.TransformerException;
import lucee.transformer.bytecode.Body;
import lucee.transformer.bytecode.Page;
import lucee.transformer.bytecode.Statement;
import lucee.transformer.bytecode.StaticBody;
import lucee.transformer.bytecode.statement.tag.Attribute;
import lucee.transformer.bytecode.statement.tag.Tag;
import lucee.transformer.bytecode.util.ASMUtil;
import lucee.transformer.cfml.evaluator.EvaluatorException;
import lucee.transformer.cfml.evaluator.EvaluatorSupport;
import lucee.transformer.expression.ExprString;
import lucee.transformer.expression.Expression;
import lucee.transformer.expression.literal.LitBoolean;
import lucee.transformer.expression.literal.LitString;
import lucee.transformer.expression.literal.Literal;
import lucee.transformer.library.function.FunctionLib;
import lucee.transformer.library.function.FunctionLibFunction;
import lucee.transformer.library.tag.TagLibTag;
import lucee.transformer.util.PageSourceCode;
import lucee.transformer.util.SourceCode;
/**
* Prueft den Kontext des Tag function.
* Das Attribute <code>argument</code> darf nur direkt innerhalb des Tag <code>function</code> liegen.
* Dem Tag <code>argument</code> muss als erstes im tag function vorkommen
*/
public final class Function extends EvaluatorSupport {
@Override
public void evaluate(Tag tag, TagLibTag libTag, FunctionLib[] flibs) throws EvaluatorException {
//Body p=(Body) tag.getParent();
//Statement pp = p.getParent();
boolean isCI=true;
try {
isCI = ASMUtil.getAncestorPage(tag).isComponent() || ASMUtil.getAncestorPage(tag).isInterface();
} catch (TransformerException e) {}
Attribute attrName = tag.getAttribute("name");
if(attrName!=null) {
Expression expr = attrName.getValue();
PageSource ps=null;
if(expr instanceof LitString && !isCI){
Page p = ASMUtil.getAncestorPage(tag,null);
if(p!=null) {
SourceCode sc = p.getSourceCode();
if(sc instanceof PageSourceCode){
PageSourceCode psc=(PageSourceCode) sc;
ps=psc.getPageSource();
}
}
checkFunctionName(((LitString)expr).getString(),flibs,ps);
}
}
// attribute modifier
boolean isStatic=false;
{
Attribute attrModifier = tag.getAttribute("modifier");
if(attrModifier!=null) {
ExprString expr = tag.getFactory().toExprString(attrModifier.getValue());
if(!(expr instanceof Literal))
throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract,final,static]");
String modifier=StringUtil.emptyIfNull(((Literal)expr).getString()).trim();
if(!StringUtil.isEmpty(modifier) &&
!"abstract".equalsIgnoreCase(modifier) && !"final".equalsIgnoreCase(modifier) && !"static".equalsIgnoreCase(modifier))
throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract,final,static]");
isStatic="static".equalsIgnoreCase(modifier);
boolean abstr = "abstract".equalsIgnoreCase(modifier);
if(abstr)throwIfNotEmpty(tag);
}
}
// cachedWithin
{
Attribute attrCachedWithin = tag.getAttribute("cachedwithin");
if(attrCachedWithin!=null) {
Expression val = attrCachedWithin.getValue();
tag.addAttribute(new Attribute(
attrCachedWithin.isDynamicType(),
attrCachedWithin.getName(),
ASMUtil.cachedWithinValue(val),
attrCachedWithin.getType()));
}
}
// Attribute localMode
{
Attribute attrLocalMode = tag.getAttribute("localmode");
if(attrLocalMode!=null) {
Expression expr = attrLocalMode.getValue();
String str = ASMUtil.toString(expr,null);
if(!StringUtil.isEmpty(str) && AppListenerUtil.toLocalMode(str, -1)==-1)
throw new EvaluatorException("Attribute localMode of the Tag Function, must be a literal value (modern, classic, true or false)");
//boolean output = ((LitBoolean)expr).getBooleanValue();
//if(!output) ASMUtil.removeLiterlChildren(tag, true);
}
}
// Attribute Output
{
Attribute attrOutput = tag.getAttribute("output");
if(attrOutput!=null) {
Expression expr = tag.getFactory().toExprBoolean(attrOutput.getValue());
if(!(expr instanceof LitBoolean))
throw new EvaluatorException("Attribute output of the Tag Function, must be a literal boolean value (true or false, yes or no)");
}
}
// Buffer output
{
Attribute attrBufferOutput = tag.getAttribute("bufferoutput");
if(attrBufferOutput!=null) {
Expression expr = tag.getFactory().toExprBoolean(attrBufferOutput.getValue());
if(!(expr instanceof LitBoolean))
throw new EvaluatorException("Attribute bufferOutput of the Tag Function, must be a literal boolean value (true or false, yes or no)");
}
}
// check attribute values
Map<String, Attribute> attrs = tag.getAttributes();
Iterator<Attribute> it = attrs.values().iterator();
while(it.hasNext()) {
checkAttributeValue(tag,it.next());
}
// add to static scope
if(isStatic) {
// remove that tag from parent
ASMUtil.remove(tag);
Body body=(Body) tag.getParent();
StaticBody sb=Static.getStaticBody(body);
sb.addStatement(tag);
}
}
public static void checkFunctionName(String name, FunctionLib[] flibs,PageSource ps) throws EvaluatorException {
FunctionLibFunction flf;
for (int i = 0; i < flibs.length; i++) {
flf = flibs[i].getFunction(name);
if(flf!=null && flf.getFunctionClassDefinition().getClazz(null)!=CFFunction.class) {
String path=null;
if(ps!=null) {
path = ps.getDisplayPath();
path=path.replace('\\', '/');
}
if(path==null || path.indexOf("/library/function/")==-1)// TODO make better
throw new EvaluatorException("The name ["+name+"] is already used by a built in Function");
}
}
}
public static void throwIfNotEmpty(Tag tag) throws EvaluatorException {
Body body = tag.getBody();
List<Statement> statments = body.getStatements();
Statement stat;
Iterator<Statement> it = statments.iterator();
TagLibTag tlt;
while(it.hasNext()) {
stat=it.next();
if(stat instanceof Tag) {
tlt = ((Tag)stat).getTagLibTag();
if(!tlt.getTagClassDefinition().isClassNameEqualTo("lucee.runtime.tag.Argument"))
throw new EvaluatorException("tag "+tlt.getFullName()+" is not allowed inside a function declaration");
}
/*else if(stat instanceof PrintOut) {
//body.remove(stat);
}*/
}
}
private void checkAttributeValue(Tag tag, Attribute attr) throws EvaluatorException {
if(!(attr.getValue() instanceof Literal))
throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] must be a literal/constant value");
}
}