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
021
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import lucee.commons.lang.StringUtil;
027import lucee.runtime.PageSource;
028import lucee.runtime.functions.system.CFFunction;
029import lucee.runtime.listener.AppListenerUtil;
030import lucee.transformer.bytecode.Body;
031import lucee.transformer.bytecode.BytecodeException;
032import lucee.transformer.bytecode.Literal;
033import lucee.transformer.bytecode.Page;
034import lucee.transformer.bytecode.Statement;
035import lucee.transformer.bytecode.cast.CastBoolean;
036import lucee.transformer.bytecode.cast.CastString;
037import lucee.transformer.bytecode.expression.ExprString;
038import lucee.transformer.bytecode.expression.Expression;
039import lucee.transformer.bytecode.literal.LitBoolean;
040import lucee.transformer.bytecode.literal.LitString;
041import lucee.transformer.bytecode.statement.tag.Attribute;
042import lucee.transformer.bytecode.statement.tag.Tag;
043import lucee.transformer.bytecode.util.ASMUtil;
044import lucee.transformer.cfml.evaluator.EvaluatorException;
045import lucee.transformer.cfml.evaluator.EvaluatorSupport;
046import lucee.transformer.library.function.FunctionLib;
047import lucee.transformer.library.function.FunctionLibFunction;
048import lucee.transformer.library.tag.TagLibTag;
049
050/**
051 * Prueft den Kontext des Tag function.
052 * Das Attribute <code>argument</code> darf nur direkt innerhalb des Tag <code>function</code> liegen.
053 * Dem Tag <code>argument</code> muss als erstes im tag function vorkommen
054 */
055public final class Function extends EvaluatorSupport {
056
057        /**
058         * @see lucee.transformer.cfml.evaluator.EvaluatorSupport#evaluate(org.w3c.dom.Element, lucee.transformer.library.tag.TagLibTag)
059         */
060        public void evaluate(Tag tag, TagLibTag libTag, FunctionLib[] flibs) throws EvaluatorException {
061                //Body p=(Body) tag.getParent();
062                //Statement pp = p.getParent();
063                
064                boolean isCI=true;
065        try {
066                        isCI = ASMUtil.getAncestorPage(tag).isComponent() || ASMUtil.getAncestorPage(tag).isInterface();
067                } catch (BytecodeException e) {}
068
069                Attribute attrName = tag.getAttribute("name");
070                if(attrName!=null) {
071                        Expression expr = attrName.getValue();
072                        if(expr instanceof LitString && !isCI){
073                                Page p = ASMUtil.getAncestorPage(tag,null);
074                                checkFunctionName(((LitString)expr).getString(),flibs,p!=null?p.getPageSource():null);
075                        }
076                                
077                }
078                // attribute modifier
079                Attribute attrModifier = tag.getAttribute("modifier");
080                if(attrModifier!=null) {
081                        ExprString expr = CastString.toExprString(attrModifier.getValue());
082                        if(!(expr instanceof Literal))
083                                throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract] or [final]");
084                        String modifier=StringUtil.emptyIfNull(((Literal)expr).getString()).trim();
085                        if(!StringUtil.isEmpty(modifier) && !"abstract".equalsIgnoreCase(modifier) && !"final".equalsIgnoreCase(modifier))
086                                throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract] or [final]");
087                        
088                        
089                        boolean abstr = "abstract".equalsIgnoreCase(modifier);
090                        if(abstr)throwIfNotEmpty(tag);
091                }
092                
093                // cachedWithin
094                Attribute attrCachedWithin = tag.getAttribute("cachedwithin");
095                if(attrCachedWithin!=null) {
096                        Expression val = attrCachedWithin.getValue();
097                        tag.addAttribute(new Attribute(
098                                        attrCachedWithin.isDynamicType(), 
099                                        attrCachedWithin.getName(), 
100                                        ASMUtil.cachedWithinValue(val),
101                                        attrCachedWithin.getType()));
102                }
103                
104                // Attribute localMode
105                Attribute attrLocalMode = tag.getAttribute("localmode");
106                if(attrLocalMode!=null) {
107                        Expression expr = attrLocalMode.getValue();
108                        String str = ASMUtil.toString(expr,null);
109                        if(!StringUtil.isEmpty(str) && AppListenerUtil.toLocalMode(str, -1)==-1)
110                                throw new EvaluatorException("Attribute localMode of the Tag Function, must be a literal value (modern, classic, true or false)");
111                        //boolean output = ((LitBoolean)expr).getBooleanValue();
112                        //if(!output) ASMUtil.removeLiterlChildren(tag, true);
113                }
114                
115                
116                // Attribute Output
117                // "output=true" wird in "lucee.transformer.cfml.attributes.impl.Function" gehaendelt
118                Attribute attrOutput = tag.getAttribute("output");
119                if(attrOutput!=null) {
120                        Expression expr = CastBoolean.toExprBoolean(attrOutput.getValue());
121                        if(!(expr instanceof LitBoolean))
122                                throw new EvaluatorException("Attribute output of the Tag Function, must be a literal boolean value (true or false, yes or no)");
123                        //boolean output = ((LitBoolean)expr).getBooleanValue();
124                        //if(!output) ASMUtil.removeLiterlChildren(tag, true);
125                }
126                
127                Attribute attrBufferOutput = tag.getAttribute("bufferoutput");
128                if(attrBufferOutput!=null) {
129                        Expression expr = CastBoolean.toExprBoolean(attrBufferOutput.getValue());
130                        if(!(expr instanceof LitBoolean))
131                                throw new EvaluatorException("Attribute bufferOutput of the Tag Function, must be a literal boolean value (true or false, yes or no)");
132                        //boolean output = ((LitBoolean)expr).getBooleanValue();
133                        //if(!output) ASMUtil.removeLiterlChildren(tag, true);
134                }
135                
136                
137        //if(ASMUtil.isRoot(pp)) {
138                Map attrs = tag.getAttributes();
139                Iterator it = attrs.keySet().iterator();
140                Attribute attr;
141                while(it.hasNext()) {
142                        attr=(Attribute) attrs.get(it.next());
143                        checkAttributeValue(tag,attr);
144                }
145        //}
146        
147        }
148        
149        private static void checkFunctionName(String name, FunctionLib[] flibs,PageSource ps) throws EvaluatorException {
150                FunctionLibFunction flf;
151                for (int i = 0; i < flibs.length; i++) {
152                        flf = flibs[i].getFunction(name);
153                        if(flf!=null && flf.getClazz(null)!=CFFunction.class) {
154                                
155                                String path=null;
156                                if(ps!=null) {
157                                        path = ps.getDisplayPath();
158                                        path=path.replace('\\', '/');
159                                }
160                                if(path==null || path.indexOf("/library/function/")==-1)// TODO make better
161                                        throw new EvaluatorException("The name ["+name+"] is already used by a built in Function");
162                        }
163                }
164        }
165
166        public static void throwIfNotEmpty(Tag tag) throws EvaluatorException {
167                Body body = tag.getBody();
168                List<Statement> statments = body.getStatements();
169                Statement stat;
170                Iterator<Statement> it = statments.iterator();
171                TagLibTag tlt;
172                
173                while(it.hasNext()) {
174                        stat=it.next();
175                        if(stat instanceof Tag) {
176                                tlt = ((Tag)stat).getTagLibTag();
177                                if(!tlt.getTagClassName().equals("lucee.runtime.tag.Argument"))
178                                        throw new EvaluatorException("tag "+tlt.getFullName()+" is not allowed inside a function declaration");
179                        }
180                        /*else if(stat instanceof PrintOut) {
181                                //body.remove(stat);
182                        }*/
183                }
184        }
185
186        private void checkAttributeValue(Tag tag, Attribute attr) throws EvaluatorException {
187                if(!(attr.getValue() instanceof Literal))
188                        throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] must be a literal/constant value");
189        
190    }
191}