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.bytecode.statement.tag;
020
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import lucee.commons.lang.StringUtil;
027import lucee.runtime.type.util.ComponentUtil;
028import lucee.transformer.bytecode.Body;
029import lucee.transformer.bytecode.BodyBase;
030import lucee.transformer.bytecode.BytecodeContext;
031import lucee.transformer.bytecode.BytecodeException;
032import lucee.transformer.bytecode.Literal;
033import lucee.transformer.bytecode.Page;
034import lucee.transformer.bytecode.Position;
035import lucee.transformer.bytecode.Statement;
036import lucee.transformer.bytecode.expression.ExprString;
037import lucee.transformer.bytecode.expression.Expression;
038import lucee.transformer.bytecode.literal.LitBoolean;
039import lucee.transformer.bytecode.literal.LitString;
040import lucee.transformer.bytecode.statement.FlowControlFinal;
041import lucee.transformer.bytecode.statement.IFunction;
042import lucee.transformer.bytecode.statement.PrintOut;
043import lucee.transformer.bytecode.statement.udf.Function;
044import lucee.transformer.bytecode.statement.udf.FunctionImpl;
045 
046public final class TagFunction extends TagBase implements IFunction {
047
048        private static final ExprString ANY = LitString.toExprString("any");
049
050        private static final Expression PUBLIC = LitString.toExprString("public");
051
052        private static final Expression EMPTY = LitString.toExprString("");
053        
054        public TagFunction(Position start,Position end) {
055                super(start,end);
056                
057        }
058        
059        /**
060         * @see lucee.transformer.bytecode.statement.IFunction#writeOut(lucee.transformer.bytecode.BytecodeContext, int)
061         */
062        public void writeOut(BytecodeContext bc, int type) throws BytecodeException {
063        //ExpressionUtil.visitLine(bc, getStartLine());
064        _writeOut(bc,type);
065        //ExpressionUtil.visitLine(bc, getEndLine());
066        }
067        
068        /**
069         * @see lucee.transformer.bytecode.statement.tag.TagBase#_writeOut(lucee.transformer.bytecode.BytecodeContext)
070         */
071        public void _writeOut(BytecodeContext bc) throws BytecodeException {
072                _writeOut(bc,Function.PAGE_TYPE_REGULAR);
073        }
074
075        public void _writeOut(BytecodeContext bc, int type) throws BytecodeException {
076                
077                Body functionBody = new BodyBase();
078                Function func = createFunction(bc.getPage(),functionBody,bc.getOutput());
079                func.setParent(getParent());
080
081                List<Statement> statements = getBody().getStatements();
082                Statement stat;
083                Tag tag;
084                
085                // suppress WS between cffunction and the last cfargument
086                Tag last=null;
087                if(bc.getSupressWSbeforeArg()){
088                        // check if there is a cfargument at all
089                        Iterator<Statement> it = statements.iterator();
090                        while (it.hasNext()) {
091                                stat = it.next();
092                                if (stat instanceof Tag) {
093                                        tag = (Tag) stat;
094                                        if (tag.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Argument")) {
095                                                last=tag;
096                                        }
097                                }
098                        }
099                        
100                        // check if there are only literal WS printouts
101                        if(last!=null) {
102                                it = statements.iterator();
103                                while (it.hasNext()) {
104                                        stat = it.next();
105                                        if(stat==last) break;
106                                        
107                                        if(stat instanceof PrintOut){
108                                                PrintOut po=(PrintOut) stat;
109                                                Expression expr = po.getExpr();
110                                                if(!(expr instanceof LitString) || !StringUtil.isWhiteSpace(((LitString)expr).getString())) {
111                                                        last=null;
112                                                        break;
113                                                }
114                                        }
115                                }
116                        }
117                }
118                
119                
120                
121                Iterator<Statement> it = statements.iterator();
122                boolean beforeLastArgument=last!=null;
123                while (it.hasNext()) {
124                        stat = it.next();
125                        if(beforeLastArgument) {
126                                if(stat==last) {
127                                        beforeLastArgument=false;
128                                }
129                                else if(stat instanceof PrintOut){
130                                        PrintOut po=(PrintOut) stat;
131                                        Expression expr = po.getExpr();
132                                        if(expr instanceof LitString) {
133                                                LitString ls=(LitString) expr;
134                                                if(StringUtil.isWhiteSpace(ls.getString())) continue;
135                                        }
136                                }
137                                
138                        }
139                        if (stat instanceof Tag) {
140                                tag = (Tag) stat;
141                                if (tag.getTagLibTag().getTagClassName().equals(
142                                                "lucee.runtime.tag.Argument")) {
143                                        addArgument(func, tag);
144                                        continue;
145                                }
146                        }
147                        functionBody.addStatement(stat);
148                }
149                func._writeOut(bc,type);
150
151        }
152
153        private void addArgument(Function func, Tag tag) {
154                Attribute attr;
155                // name
156                Expression name = tag.removeAttribute("name").getValue();
157                
158                // type
159                attr = tag.removeAttribute("type");
160                Expression type = (attr == null) ? ANY : attr.getValue();
161
162                // required
163                attr = tag.removeAttribute("required");
164                Expression required = (attr == null) ? LitBoolean.FALSE : attr
165                                .getValue();
166
167                // default
168                attr = tag.removeAttribute("default");
169                Expression defaultValue = (attr == null) ? null : attr.getValue();
170                
171                // passby
172                attr = tag.removeAttribute("passby");
173                LitBoolean passByReference = LitBoolean.TRUE;
174                if(attr!=null) {
175                        // i can cast irt to LitString because he evulator check this before
176                         String str = ((LitString)attr.getValue()).getString();
177                         if(str.trim().equalsIgnoreCase("value"))
178                                 passByReference=LitBoolean.FALSE;
179                }
180                
181                
182                // displayname
183                attr = tag.removeAttribute("displayname");
184                Expression displayName = (attr == null) ? EMPTY : attr.getValue();
185
186                // hint
187                attr = tag.removeAttribute("hint");
188                if (attr == null)
189                        attr = tag.removeAttribute("description");
190                
191                Expression hint;
192                if(attr == null)hint=EMPTY;
193                else hint=attr.getValue();
194                
195                func.addArgument(name, type, required, defaultValue, passByReference,displayName, hint,tag.getAttributes());
196
197        }
198
199        private Function createFunction(Page page, Body body,boolean defaultOutput) throws BytecodeException {
200                Attribute attr;
201                
202                // name
203                Expression name = removeAttribute("name").getValue();
204                /*if(name instanceof LitString) {
205                        ((LitString)name).upperCase();
206                }*/
207                // return
208                attr = removeAttribute("returntype");
209                // if(attr==null) attr = getAttribute("return");
210                // if(attr==null) attr = getAttribute("type");
211                Expression returnType = (attr == null) ? ANY : attr.getValue();
212
213                // output
214                attr = removeAttribute("output");
215                Expression output = (attr == null) ? (defaultOutput ? LitBoolean.TRUE : LitBoolean.FALSE) : attr.getValue();
216                
217                // bufferOutput
218                attr = removeAttribute("bufferoutput");
219                Expression bufferOutput = (attr == null) ? null : attr.getValue();
220
221                // modifier
222                boolean _abstract=false,_final=false;
223                attr = removeAttribute("modifier");
224                if(attr!=null) {
225                        Expression val = attr.getValue();
226                        if(val instanceof Literal) {
227                                Literal l=(Literal) val;
228                                String str = StringUtil.emptyIfNull(l.getString()).trim();
229                                if("abstract".equalsIgnoreCase(str))_abstract=true;
230                                else if("final".equalsIgnoreCase(str))_final=true;
231                        }
232                }
233
234                // access
235                attr = removeAttribute("access");
236                Expression access = (attr == null) ? PUBLIC : attr.getValue();
237
238                // dspLabel
239                attr = removeAttribute("displayname");
240                Expression displayname = (attr == null) ? EMPTY : attr.getValue();
241
242                // hint
243                attr = removeAttribute("hint");
244                Expression hint = (attr == null) ? EMPTY : attr.getValue();
245
246                // description
247                attr = removeAttribute("description");
248                Expression description = (attr == null) ? EMPTY : attr.getValue();
249
250                // returnformat
251                attr = removeAttribute("returnformat");
252                Expression returnFormat = (attr == null) ? null : attr.getValue();
253
254                // secureJson
255                attr = removeAttribute("securejson");
256                Expression secureJson = (attr == null) ? null : attr.getValue();
257
258                // verifyClient
259                attr = removeAttribute("verifyclient");
260                Expression verifyClient = (attr == null) ? null : attr.getValue();
261
262                // localMode
263                attr = removeAttribute("localmode");
264                Expression localMode = (attr == null) ? null : attr.getValue();
265                
266                
267                
268                // cachedWithin
269                Literal cachedWithin=null;
270                attr = removeAttribute("cachedwithin");
271                if(attr!=null) {
272                        Expression val = attr.getValue();
273                        if(val instanceof Literal)
274                                cachedWithin=((Literal)val);
275                }
276                String strAccess = ((LitString)access).getString();
277                int acc = ComponentUtil.toIntAccess(strAccess,-1);
278                if(acc==-1)
279                        throw new BytecodeException("invalid access type ["+strAccess+"], access types are remote, public, package, private",getStart());
280        
281                Function func = new FunctionImpl(page,name, returnType,returnFormat, output, bufferOutput, acc, displayname,description,
282                                hint,secureJson,verifyClient,localMode,cachedWithin,_abstract,_final, body, getStart(),getEnd());
283                 
284                
285                
286                
287//               %**%
288                Map attrs = getAttributes();
289                Iterator it = attrs.entrySet().iterator();
290                HashMap<String,Attribute> metadatas=new HashMap<String,Attribute>();
291                while(it.hasNext()){
292                        attr=(Attribute) ((Map.Entry)it.next()).getValue();
293                        metadatas.put(attr.getName(),attr);
294                }
295                func.setMetaData(metadatas);
296                return func;
297        }
298        
299        @Override
300        public FlowControlFinal getFlowControlFinal() {
301                return null;
302        }
303
304}