/** * 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 lucee.commons.lang.StringUtil; import lucee.loader.engine.CFMLEngine; import lucee.runtime.config.Config; import lucee.runtime.config.ConfigImpl; import lucee.runtime.exp.TemplateException; import lucee.transformer.bytecode.BytecodeFactory; import lucee.transformer.bytecode.Page; import lucee.transformer.bytecode.Statement; import lucee.transformer.bytecode.statement.tag.Attribute; import lucee.transformer.bytecode.statement.tag.Tag; import lucee.transformer.bytecode.statement.tag.TagLoop; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.cfml.Data; import lucee.transformer.cfml.ExprTransformer; import lucee.transformer.cfml.TransfomerSettings; import lucee.transformer.cfml.evaluator.EvaluatorException; import lucee.transformer.cfml.evaluator.EvaluatorPool; import lucee.transformer.cfml.evaluator.EvaluatorSupport; import lucee.transformer.expression.Expression; import lucee.transformer.expression.literal.LitString; import lucee.transformer.library.function.FunctionLib; import lucee.transformer.library.tag.TagLib; import lucee.transformer.library.tag.TagLibTag; import lucee.transformer.util.SourceCode; public final class Loop extends EvaluatorSupport { @Override public TagLib execute(Config config,Tag tag, TagLibTag libTag, FunctionLib[] flibs,Data data) throws TemplateException { TagLoop loop=(TagLoop) tag; // label try { if(ASMUtil.isLiteralAttribute(tag, "label", ASMUtil.TYPE_STRING, false, true)) { LitString ls=(LitString) tag.getFactory().toExprString(tag.getAttribute("label").getValue()); String l = ls.getString(); if(!StringUtil.isEmpty(l,true)) { loop.setLabel(l.trim()); tag.removeAttribute("label"); } } } catch (EvaluatorException e) { throw new TemplateException(null, e); } return null; } @Override public void evaluate(Tag tag,TagLibTag tagLibTag,FunctionLib[] flibs) throws EvaluatorException { TagLoop loop=(TagLoop) tag; // attribute maxrows and endrow not allowd at the same time if(tag.containsAttribute("maxrows") && tag.containsAttribute("endrow")) throw new EvaluatorException("Wrong Context, you cannot use attribute maxrows and endrow at the same time."); // file loop if(tag.containsAttribute("file")) { if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) throw new EvaluatorException("Wrong Context, when you use attribute file you must also use attribute index and/or item"); loop.setType(TagLoop.TYPE_FILE); return; } // list loop if(tag.containsAttribute("list")){ if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) throw new EvaluatorException("Wrong Context, when you use attribute list,you must define attribute index and/or item"); loop.setType(TagLoop.TYPE_LIST); return; } // array loop if(tag.containsAttribute("array")){ if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) throw new EvaluatorException("Wrong Context, when you use attribute array, you must define attribute index and/or item"); loop.setType(TagLoop.TYPE_ARRAY); return; } // array loop if(tag.containsAttribute("times")) { if(tag.getAttributes().size()>1) throw new EvaluatorException("Wrong Context, when you use attribute times, no other attributes are allowed"); loop.setType(TagLoop.TYPE_TIMES); return; } // struct loop if(tag.containsAttribute("struct")) { if (!tag.containsAttribute("index") && !tag.containsAttribute("item") && !tag.containsAttribute("key") && !tag.containsAttribute("value")) throw new EvaluatorException("Wrong Context, when you use attribute struct, you must define attribute index (alias key) and/or item (alias value)"); loop.setType(TagLoop.TYPE_STRUCT); return; } // collection loop if(tag.containsAttribute("collection")) { if (!tag.containsAttribute("index") && !tag.containsAttribute("item") && !tag.containsAttribute("key") && !tag.containsAttribute("value")) throw new EvaluatorException("Wrong Context, when you use attribute struct, you must define attribute index (alias key) and/or item (alias value)"); loop.setType(TagLoop.TYPE_COLLECTION); return; } // index loop /*if(tag.containsAttribute("index")) { if(!tag.containsAttribute("from") || !tag.containsAttribute("to")) throw new EvaluatorException("Wrong Context, when you use attribute index you must also use attribute from and to or list or file"); loop.setType(TagLoop.TYPE_INDEX); return; }*/ if(tag.containsAttribute("from") || tag.containsAttribute("to")) { if(!tag.containsAttribute("from")) throw new EvaluatorException("Wrong Context, when you use attribute to, you must also use attribute from."); if(!tag.containsAttribute("to")) throw new EvaluatorException("Wrong Context, when you use attribute from, you must also use attribute to."); if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) throw new EvaluatorException("Wrong Context, when you use attribute from and to, you must define attribute index or item."); if(tag.containsAttribute("index") && tag.containsAttribute("item")) throw new EvaluatorException("For this type of loop, you cannot use attribute index and item at the same time."); loop.setType(TagLoop.TYPE_FROM_TO); return; } // condition loop if(tag.containsAttribute("condition")){ if(tag.isScriptBase()) throw new EvaluatorException("tag loop-condition is not supported within cfscript, use instead a while statement."); TagLib tagLib=tagLibTag.getTagLib(); ExprTransformer transformer; String text=ASMUtil.getAttributeString(tag, "condition"); try { transformer = tagLib.getExprTransfomer(); Page page = ASMUtil.getAncestorPage(tag); ConfigImpl config=(ConfigImpl) page.getConfig(); Expression expr=transformer.transform( BytecodeFactory.getInstance(config), page, new EvaluatorPool(),null,flibs, config.getCoreTagLib(page.getSourceCode().getDialect()).getScriptTags(), new SourceCode(text,false,page.getSourceCode().getDialect()), new TransfomerSettings( page.getSourceCode().getDialect()==CFMLEngine.DIALECT_CFML && config.getDotNotationUpperCase(), page.getSourceCode().getDialect()==CFMLEngine.DIALECT_CFML && config.getHandleUnQuotedAttrValueAsString(),page.ignoreScopes)); tag.addAttribute(new Attribute(false,"condition",page.getFactory().toExprBoolean(expr),"boolean")); } catch (Exception e) {e.printStackTrace(); throw new EvaluatorException(e.getMessage()); } loop.setType(TagLoop.TYPE_CONDITION); return; } // query loop if(tag.containsAttribute("query")){ loop.setType(TagLoop.TYPE_QUERY); return; } Info info=getParentInfo(loop); // query group if(tag.containsAttribute("group") && info.hasParentWithQuery){ loop.setType(TagLoop.TYPE_GROUP); return; } if(info.hasParentWithQuery) { if(info.hasParentWithGroup) loop.setType(TagLoop.TYPE_INNER_GROUP); else loop.setType(TagLoop.TYPE_INNER_QUERY); return; } /* if(hasQuery) output.setType(TagOutput.TYPE_QUERY); else if(tag.containsAttribute("group") && hasParentWithQuery) output.setType(TagOutput.TYPE_GROUP); else if(hasParentWithQuery) { if(hasParentWithGroup) output.setType(TagOutput.TYPE_INNER_GROUP); else output.setType(TagOutput.TYPE_INNER_QUERY); } else output.setType(TagOutput.TYPE_NORMAL); */ loop.setType(TagLoop.TYPE_NOTHING); //throw new EvaluatorException("Wrong Context, invalid attributes in tag cfloop"); } private Info getParentInfo(TagLoop loop) { // check if inside a query tag TagLoop parent = loop; Info info=new Info(); info.hasParentWithGroup=false; info.hasParentWithQuery=false; //boolean hasQuery=loop.containsAttribute("query"); while((parent=getParentTagLoop(parent))!=null) { if(!info.hasParentWithQuery)info.hasParentWithQuery=parent.hasQuery(); if(!info.hasParentWithGroup)info.hasParentWithGroup=parent.hasGroup(); if(info.hasParentWithQuery && info.hasParentWithGroup)break; } return info; } private static TagLoop getParentTagLoop(TagLoop stat) { Statement parent = stat; while(true) { parent=parent.getParent(); if(parent==null)return null; if(parent instanceof TagLoop) return (TagLoop) parent; } } class Info { private boolean hasParentWithGroup=false; private boolean hasParentWithQuery=false; } }