001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.transformer.cfml.evaluator.impl; 020 021import lucee.commons.io.CharsetUtil; 022import lucee.commons.lang.StringUtil; 023import lucee.runtime.config.Config; 024import lucee.runtime.config.ConfigImpl; 025import lucee.runtime.engine.ThreadLocalPageContext; 026import lucee.runtime.exp.TemplateException; 027import lucee.transformer.bytecode.Page; 028import lucee.transformer.bytecode.Statement; 029import lucee.transformer.bytecode.cast.CastBoolean; 030import lucee.transformer.bytecode.cast.CastString; 031import lucee.transformer.bytecode.expression.Expression; 032import lucee.transformer.bytecode.literal.LitString; 033import lucee.transformer.bytecode.statement.tag.Attribute; 034import lucee.transformer.bytecode.statement.tag.Tag; 035import lucee.transformer.bytecode.statement.tag.TagLoop; 036import lucee.transformer.bytecode.util.ASMUtil; 037import lucee.transformer.cfml.Data; 038import lucee.transformer.cfml.ExprTransformer; 039import lucee.transformer.cfml.TransfomerSettings; 040import lucee.transformer.cfml.evaluator.EvaluatorException; 041import lucee.transformer.cfml.evaluator.EvaluatorSupport; 042import lucee.transformer.library.function.FunctionLib; 043import lucee.transformer.library.tag.TagLib; 044import lucee.transformer.library.tag.TagLibTag; 045import lucee.transformer.util.CFMLString; 046 047public final class Loop extends EvaluatorSupport { 048 049 050 public TagLib execute(Config config,Tag tag, TagLibTag libTag, FunctionLib[] flibs,Data data) throws TemplateException { 051 TagLoop loop=(TagLoop) tag; 052 // label 053 try { 054 if(ASMUtil.isLiteralAttribute(tag, "label", ASMUtil.TYPE_STRING, false, true)) { 055 LitString ls=(LitString) CastString.toExprString(tag.getAttribute("label").getValue()); 056 String l = ls.getString(); 057 if(!StringUtil.isEmpty(l,true)) { 058 loop.setLabel(l.trim()); 059 tag.removeAttribute("label"); 060 } 061 } 062 } 063 catch (EvaluatorException e) { 064 throw new TemplateException(null, e); 065 } 066 return null; 067 } 068 069 /** 070 * 071 * @see lucee.transformer.cfml.evaluator.EvaluatorSupport#evaluate(lucee.transformer.bytecode.statement.tag.Tag, lucee.transformer.library.tag.TagLibTag, lucee.transformer.library.function.FunctionLib[]) 072 */ 073 public void evaluate(Tag tag,TagLibTag tagLibTag,FunctionLib[] flibs) throws EvaluatorException { 074 TagLoop loop=(TagLoop) tag; 075 076 // attribute maxrows and endrow not allowd at the same time 077 if(tag.containsAttribute("maxrows") && tag.containsAttribute("endrow")) 078 throw new EvaluatorException("Wrong Context, you cannot use attribute maxrows and endrow at the same time."); 079 080 // file loop 081 if(tag.containsAttribute("file")) { 082 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 083 throw new EvaluatorException("Wrong Context, when you use attribute file you must also use attribute index and/or item"); 084 loop.setType(TagLoop.TYPE_FILE); 085 return; 086 } 087 // list loop 088 if(tag.containsAttribute("list")){ 089 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 090 throw new EvaluatorException("Wrong Context, when you use attribute list,you must define attribute index and/or item"); 091 loop.setType(TagLoop.TYPE_LIST); 092 return; 093 } 094 // array loop 095 if(tag.containsAttribute("array")){ 096 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 097 throw new EvaluatorException("Wrong Context, when you use attribute array, you must define attribute index and/or item"); 098 loop.setType(TagLoop.TYPE_ARRAY); 099 return; 100 } 101 102 // array loop 103 if(tag.containsAttribute("times")) { 104 105 if(tag.getAttributes().size()>1) 106 throw new EvaluatorException("Wrong Context, when you use attribute times, no other attributes are allowed"); 107 loop.setType(TagLoop.TYPE_TIMES); 108 return; 109 } 110 111 // struct loop 112 if(tag.containsAttribute("struct")) { 113 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 114 throw new EvaluatorException("Wrong Context, when you use attribute struct,you must define attribute index and/or item"); 115 loop.setType(TagLoop.TYPE_STRUCT); 116 return; 117 } 118 119 // collection loop 120 if(tag.containsAttribute("collection")) { 121 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 122 throw new EvaluatorException("Wrong Context, when you use attribute collection,you must define attribute index and/or item"); 123 loop.setType(TagLoop.TYPE_COLLECTION); 124 return; 125 } 126 // index loop 127 /*if(tag.containsAttribute("index")) { 128 if(!tag.containsAttribute("from") || !tag.containsAttribute("to")) 129 throw new EvaluatorException("Wrong Context, when you use attribute index you must also use attribute from and to or list or file"); 130 loop.setType(TagLoop.TYPE_INDEX); 131 return; 132 }*/ 133 if(tag.containsAttribute("from") || tag.containsAttribute("to")) { 134 if(!tag.containsAttribute("from")) 135 throw new EvaluatorException("Wrong Context, when you use attribute to, you must also use attribute from."); 136 if(!tag.containsAttribute("to")) 137 throw new EvaluatorException("Wrong Context, when you use attribute from, you must also use attribute to."); 138 if(!tag.containsAttribute("index") && !tag.containsAttribute("item")) 139 throw new EvaluatorException("Wrong Context, when you use attribute from and to, you must define attribute index or item."); 140 141 if(tag.containsAttribute("index") && tag.containsAttribute("item")) 142 throw new EvaluatorException("For this type of loop, you cannot use attribute index and item at the same time."); 143 144 145 loop.setType(TagLoop.TYPE_FROM_TO); 146 return; 147 } 148 149 150 // condition loop 151 if(tag.containsAttribute("condition")){ 152 if(tag.isScriptBase()) 153 throw new EvaluatorException("tag loop-condition is not supported within cfscript, use instead a while statement."); 154 155 TagLib tagLib=tagLibTag.getTagLib(); 156 ExprTransformer transformer; 157 String text=ASMUtil.getAttributeString(tag, "condition"); 158 159 try { 160 ConfigImpl config=(ConfigImpl) ThreadLocalPageContext.getConfig(); 161 162 transformer = tagLib.getExprTransfomer(); 163 Page page = ASMUtil.getAncestorPage(tag); 164 Expression expr=transformer.transform(ASMUtil.getAncestorPage(tag),null,null,flibs,config.getCoreTagLib().getScriptTags(),new CFMLString(text,CharsetUtil.UTF8),TransfomerSettings.toSetting(page.getPageSource().getMapping(),null)); 165 tag.addAttribute(new Attribute(false,"condition",CastBoolean.toExprBoolean(expr),"boolean")); 166 } 167 catch (Exception e) { 168 throw new EvaluatorException(e.getMessage()); 169 } 170 loop.setType(TagLoop.TYPE_CONDITION); 171 return; 172 } 173 // query loop 174 if(tag.containsAttribute("query")){ 175 loop.setType(TagLoop.TYPE_QUERY); 176 return; 177 } 178 Info info=getParentInfo(loop); 179 // query group 180 if(tag.containsAttribute("group") && info.hasParentWithQuery){ 181 loop.setType(TagLoop.TYPE_GROUP); 182 return; 183 } 184 185 if(info.hasParentWithQuery) { 186 if(info.hasParentWithGroup) loop.setType(TagLoop.TYPE_INNER_GROUP); 187 else loop.setType(TagLoop.TYPE_INNER_QUERY); 188 return; 189 } 190 /* 191 if(hasQuery) 192 output.setType(TagOutput.TYPE_QUERY); 193 194 else if(tag.containsAttribute("group") && hasParentWithQuery) 195 output.setType(TagOutput.TYPE_GROUP); 196 197 else if(hasParentWithQuery) { 198 if(hasParentWithGroup) output.setType(TagOutput.TYPE_INNER_GROUP); 199 else output.setType(TagOutput.TYPE_INNER_QUERY); 200 } 201 else 202 output.setType(TagOutput.TYPE_NORMAL); 203 204 205 */ 206 207 loop.setType(TagLoop.TYPE_NOTHING); 208 //throw new EvaluatorException("Wrong Context, invalid attributes in tag cfloop"); 209 210 } 211 212 private Info getParentInfo(TagLoop loop) { 213 214 // check if inside a query tag 215 TagLoop parent = loop; 216 Info info=new Info(); 217 info.hasParentWithGroup=false; 218 info.hasParentWithQuery=false; 219 //boolean hasQuery=loop.containsAttribute("query"); 220 221 while((parent=getParentTagLoop(parent))!=null) { 222 if(!info.hasParentWithQuery)info.hasParentWithQuery=parent.hasQuery(); 223 if(!info.hasParentWithGroup)info.hasParentWithGroup=parent.hasGroup(); 224 if(info.hasParentWithQuery && info.hasParentWithGroup)break; 225 } 226 return info; 227 } 228 229 230 231 private static TagLoop getParentTagLoop(TagLoop stat) { 232 Statement parent = stat; 233 while(true) { 234 parent=parent.getParent(); 235 if(parent==null)return null; 236 if(parent instanceof TagLoop) return (TagLoop) parent; 237 } 238 } 239 240 class Info { 241 private boolean hasParentWithGroup=false; 242 private boolean hasParentWithQuery=false; 243 } 244}