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.runtime.functions.system;
020
021import java.util.Iterator;
022
023import lucee.commons.io.res.Resource;
024import lucee.commons.io.res.util.ResourceUtil;
025import lucee.commons.lang.ExceptionUtil;
026import lucee.runtime.PageContext;
027import lucee.runtime.PageContextImpl;
028import lucee.runtime.PageSource;
029import lucee.runtime.config.ConfigWeb;
030import lucee.runtime.converter.JSONConverter;
031import lucee.runtime.exp.FunctionException;
032import lucee.runtime.exp.PageException;
033import lucee.runtime.ext.function.Function;
034import lucee.runtime.op.Caster;
035import lucee.runtime.type.Array;
036import lucee.runtime.type.ArrayImpl;
037import lucee.runtime.type.Collection;
038import lucee.runtime.type.KeyImpl;
039import lucee.runtime.type.Struct;
040import lucee.runtime.type.StructImpl;
041import lucee.runtime.type.UDF;
042import lucee.runtime.type.util.KeyConstants;
043
044/**
045 * returns the root of this actuell Page Context
046 */
047public final class CallStackGet implements Function {
048
049        private static final long serialVersionUID = -5853145189662102420L;
050        static final Collection.Key LINE_NUMBER = KeyImpl.init("LineNumber");
051
052        public static Object call(PageContext pc) {
053                Array arr=new ArrayImpl();
054                _getTagContext(pc, arr, new Exception("Stack trace"),LINE_NUMBER);
055                return arr;
056        }
057
058        public static Object call(PageContext pc, String type) throws PageException {
059
060                Array arr = (Array)call(pc);
061
062                if ( type.equalsIgnoreCase( "array" ) )
063                        return arr;
064
065                if ( type.equalsIgnoreCase( "json" ) ) {
066                        try {
067                                return new JSONConverter(true,null).serialize( pc, arr, false );
068                        }
069                        catch (Throwable t) {
070                        ExceptionUtil.rethrowIfNecessary(t);
071                                throw Caster.toPageException( t );
072                        }
073                }
074
075                StringBuilder sb = new StringBuilder( 64 * arr.size() );
076                Struct struct;
077                String func;
078
079                Iterator it = arr.valueIterator();
080
081                if ( type.equalsIgnoreCase( "text" ) || type.equalsIgnoreCase( "string" ) ) {
082
083                        while (it.hasNext()) {
084
085                                struct = (Struct)it.next();
086
087                                sb.append( (String)struct.get( KeyConstants._template ) );
088
089                                func = (String)struct.get( KeyConstants._function );
090                                if ( !func.isEmpty() ) {
091                                        sb.append( '.' ).append( func ).append( "()" );
092                                }
093
094                                sb.append( ':' ).append( ((Double)struct.get( LINE_NUMBER )).intValue() );
095
096                                if ( it.hasNext() )
097                                        sb.append( "; " );
098                        }
099
100                        return sb.toString();
101                }
102
103                if ( type.equalsIgnoreCase( "html" ) ) {
104
105                        sb.append( "<ul class='-lucee-array'>" );
106
107                        while (it.hasNext()) {
108
109                                struct = (Struct)it.next();
110
111                                sb.append( "<li>" );
112
113                                sb.append( (String)struct.get( KeyConstants._template ) );
114
115                                func = (String)struct.get( KeyConstants._function );
116                                if ( !func.isEmpty() ) {
117                                        sb.append( '.' ).append( func ).append( "()" );
118                                }
119
120                                sb.append( ':' ).append( ((Double)struct.get( LINE_NUMBER )).intValue() );
121
122                                sb.append( "</li>" );
123                        }
124
125                        sb.append("</ul>");
126
127                        return sb.toString();
128                }
129
130                throw new FunctionException( pc, CallStackGet.class.getSimpleName(), 1, "type", "Argument type [" + type +"] is not valid.  Valid types are: [array], text, html, json." );
131        }
132
133        public static void _getTagContext(PageContext pc, Array tagContext, Throwable t,Collection.Key lineNumberName) {
134                //Throwable root = t.getRootCause();
135                Throwable cause = t.getCause();
136                if(cause!=null)_getTagContext(pc, tagContext, cause,lineNumberName);
137                StackTraceElement[] traces = t.getStackTrace();
138
139                UDF[] udfs = ((PageContextImpl)pc).getUDFs();
140
141                int line=0;
142                String template;
143                Struct item;
144                StackTraceElement trace=null;
145                String functionName,methodName;
146                int index=udfs.length-1;
147                for(int i=0;i<traces.length;i++) {
148                        trace=traces[i];
149                        template=trace.getFileName();
150                        if(trace.getLineNumber()<=0 || template==null || ResourceUtil.getExtension(template,"").equals("java")) continue;
151                        methodName=trace.getMethodName();
152                        if(methodName!=null && methodName.startsWith("udfCall") && index>-1) 
153                                functionName=udfs[index--].getFunctionName();
154
155                        else functionName="";
156
157                        item=new StructImpl();
158                        line=trace.getLineNumber();
159                        item.setEL(KeyConstants._function,functionName);
160                        
161                        /* only necessary when path is relative
162                        try {
163                                template=ExpandPath.call(pc, template);
164                        }catch (PageException e) {}*/
165                        // TODO AbsRel
166                        item.setEL(KeyConstants._template,abs((PageContextImpl) pc,template));
167                        item.setEL(lineNumberName,new Double(line));
168                        tagContext.appendEL(item);
169                }
170        }
171
172        private static String abs(PageContextImpl pc, String template) {
173                ConfigWeb config = pc.getConfig();
174                
175                Resource res = config.getResource(template);
176                if(res.exists()) return template;
177                
178                PageSource ps = pc==null?null:pc.getPageSource(template);
179                res = ps==null?null:ps.getPhyscalFile();
180                if(res==null || !res.exists()) {
181                        res=config.getResource(ps.getDisplayPath());
182                        if(res!=null && res.exists()) return res.getAbsolutePath();
183                }
184                else return res.getAbsolutePath();
185                return template;
186        }
187}