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}