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 **/
019/**
020 * Implements the CFML Function dump
021 */
022package lucee.runtime.functions.other;
023
024import java.util.Set;
025
026import lucee.commons.digest.HashUtil;
027import lucee.commons.lang.ExceptionUtil;
028import lucee.commons.lang.StringUtil;
029import lucee.commons.lang.types.RefBoolean;
030import lucee.commons.lang.types.RefBooleanImpl;
031import lucee.runtime.ComponentImpl;
032import lucee.runtime.PageContext;
033import lucee.runtime.dump.DumpData;
034import lucee.runtime.dump.DumpProperties;
035import lucee.runtime.dump.DumpRow;
036import lucee.runtime.dump.DumpTable;
037import lucee.runtime.dump.DumpUtil;
038import lucee.runtime.dump.SimpleDumpData;
039import lucee.runtime.ext.function.Function;
040import lucee.runtime.functions.string.Len;
041import lucee.runtime.op.Caster;
042import lucee.runtime.op.Decision;
043import lucee.runtime.type.Collection;
044import lucee.runtime.type.Collection.Key;
045import lucee.runtime.type.KeyImpl;
046import lucee.runtime.type.Query;
047import lucee.runtime.type.QueryImpl;
048import lucee.runtime.type.Struct;
049import lucee.runtime.type.StructImpl;
050import lucee.runtime.type.scope.Scope;
051import lucee.runtime.type.util.KeyConstants;
052import lucee.runtime.type.util.ListUtil;
053import lucee.runtime.type.util.StructUtil;
054
055public final class DumpStruct implements Function {
056        
057        public static Struct call(PageContext pc , Object object) {
058                return call(pc, object,9999,null,null,9999,true);
059        }
060
061        public static Struct call(PageContext pc , Object object, double maxLevel) {
062                return call(pc, object,maxLevel,null,null,9999,true,true,null);
063        }
064
065        public static Struct call(PageContext pc , Object object, double maxLevel, String show) {
066                return call(pc, object,maxLevel,show,null,9999,true,true,null);
067        }
068
069        public static Struct call(PageContext pc , Object object, double maxLevel, String show, String hide) {
070                return call(pc,object,maxLevel,show,hide,9999,true,true,null);
071        }
072
073        public static Struct call(PageContext pc , Object object, double maxLevel, String show, String hide,double keys) {
074                return call(pc , object, maxLevel, show, hide,keys,true,true,null);
075        }
076        public static Struct call(PageContext pc , Object object, double maxLevel, String show, String hide,double keys,boolean metainfo) {
077                return call(pc , object, maxLevel, show, hide,keys,metainfo,true,null); 
078        }
079        
080        public static Struct call(PageContext pc , Object object, double maxLevel, String show, String hide,double keys,boolean metainfo, boolean showUDFs) {
081                return call(pc , object, maxLevel, show, hide,keys,metainfo,showUDFs,null);     
082        }
083                
084        public static Struct call(PageContext pc,Object object,double maxLevel, String show, String hide,double keys,boolean metainfo, boolean showUDFs, String label) {
085                if(show!=null && "all".equalsIgnoreCase(show.trim()))show=null;
086                if(hide!=null && "all".equalsIgnoreCase(hide.trim()))hide=null;
087                
088                Set setShow=(show!=null)?ListUtil.listToSet(show.toLowerCase(),",",true):null;
089                Set setHide=(hide!=null)?ListUtil.listToSet(hide.toLowerCase(),",",true):null;
090                
091                DumpProperties properties=new DumpProperties((int)maxLevel,setShow,setHide,(int)keys,metainfo,showUDFs);
092                DumpData dd = DumpUtil.toDumpData(object, pc,(int)maxLevel,properties);
093
094                if(!StringUtil.isEmpty(label)) {
095                        DumpTable table=new DumpTable("#ffffff","#cccccc","#000000");
096                        table.appendRow(1,new SimpleDumpData(label));
097                        table.appendRow(0,dd);
098                        dd=table;
099                }
100                RefBoolean hasReference=new RefBooleanImpl(false);
101                Struct sct = toStruct(dd,object,hasReference);
102                sct.setEL("hasReference", hasReference.toBoolean());
103
104                addMetaData(sct, object);
105
106                return sct;
107        }
108
109        private static void addMetaData(Struct sct, Object o) {
110
111                String simpleType  = "unknown";                             // simpleType will replace colorId and colors
112                String simpleValue = "";
113
114                try {
115
116                        if (o == null) {
117                                simpleType  = "null";
118                        }
119                        else if (o instanceof Scope) {
120                                simpleType  = "struct";
121                                simpleValue = "Scope (" + getSize(o) + ")";
122                        }
123                        else if (Decision.isQuery(o)) {
124                                simpleType  = "query";
125                                simpleValue = "Query (" + getSize(o) + ")";
126                        }
127                        else if (Decision.isComponent(o)) {
128                                simpleType  = "component";
129                                simpleValue = "Component: " + ((ComponentImpl)o).getDisplayName();
130                        }
131                        else if (Decision.isFunction(o) || Decision.isUserDefinedFunction(o) || Decision.isClosure(o)) {
132                                simpleType  = "function";
133//                              simpleValue = "Function: " + ((Function)o).();      // TODO: add signature
134                        }
135            else if (Decision.isStruct(o)) {
136                simpleType  = "struct";
137                simpleValue = "Struct (" + getSize(o) + ")";
138            }
139            else if (Decision.isArray(o)) {
140                simpleType  = "array";
141                simpleValue = "Array (" + getSize(o) + ")";
142            }
143                        else if (Decision.isDate(o, false)) {
144                                simpleType  = "date";
145                                simpleValue = o.toString();
146                        }
147                        else if (Decision.isBoolean(o, false)) {
148                                simpleType  = "boolean";
149                                simpleValue = o.toString();
150                        }
151                        else if (Decision.isInteger(o, false)) {
152                                simpleType  = "numeric";
153                                simpleValue = Caster.toInteger(o).toString();
154                        }
155                        else if (Decision.isNumeric(o, false)) {
156                                simpleType  = "numeric";
157                                simpleValue = o.toString();
158                        }
159                        else if (Decision.isSimpleValue(o)) {
160                                simpleType  = "string";
161                                simpleValue = Caster.toString(o);
162                                if (simpleValue.length() > 64)
163                                        simpleValue = "String (" + simpleValue.length() + ")";
164                        }
165                        else {
166                                simpleType  = o.getClass().getSimpleName().toLowerCase();
167                        }
168
169                }
170                catch (Throwable t) {
171                        ExceptionUtil.rethrowIfNecessary(t);
172                        simpleValue = "{error}";
173                }
174
175                sct.setEL("simpleType", simpleType);
176                sct.setEL("simpleValue", simpleValue);
177        }
178
179        private static String getSize(Object o) {
180
181                return Caster.toInteger( Len.invoke(o, 0) ).toString();
182        }
183
184        private static Struct toStruct(DumpData dd, Object object, RefBoolean hasReference) {
185                DumpTable table;
186                if(dd instanceof DumpTable) table=(DumpTable) dd;
187                else {
188                        if(dd==null) dd= new SimpleDumpData("null");
189                        table=new DumpTable("#ffffff","#cccccc","#000000");
190                        table.appendRow(1,dd);
191                }
192                return toCFML(table,object,hasReference,null);
193        }
194        
195        
196        private static Object toCFML(DumpData dd, Object object, RefBoolean hasReference, Struct colors) {
197                if(dd instanceof DumpTable)return toCFML((DumpTable) dd,object,hasReference,colors);
198                if(dd==null) return new SimpleDumpData("null");
199                return dd.toString();
200        }
201        
202        private static Struct toCFML(DumpTable dt, Object object, RefBoolean hasReference, Struct colors) {
203                
204                Struct sct=new StructImpl();
205                if(colors==null) {
206                        colors=new StructImpl();
207                        sct.setEL("colors", colors);
208                }
209                Collection.Key type;
210                if(dt.getType()!=null) type=KeyImpl.init(dt.getType());
211                else if(object!=null) type=KeyImpl.init(object.getClass().getName());
212                else type=KeyConstants._null;
213                
214                // colors
215                String borderColor = toShortColor(dt.getBorderColor());
216                String fontColor = toShortColor(dt.getFontColor());
217                String highLightColor = toShortColor(dt.getHighLightColor());
218                String normalColor = toShortColor(dt.getNormalColor());
219                // create color id
220                Key colorId = KeyImpl.init(Long.toString(HashUtil.create64BitHash(new StringBuilder(borderColor)
221                .append(':').append(fontColor)
222                .append(':').append(highLightColor)
223                .append(':').append(normalColor)),Character.MAX_RADIX));
224                
225                
226                if(!colors.containsKey(colorId)) {
227                        Struct color=new StructImpl();
228                        StructUtil.setELIgnoreWhenNull(color,"borderColor", borderColor);
229                        StructUtil.setELIgnoreWhenNull(color,"fontColor", fontColor);
230                        StructUtil.setELIgnoreWhenNull(color,"highLightColor", highLightColor);
231                        StructUtil.setELIgnoreWhenNull(color,"normalColor", normalColor);
232                        colors.setEL(colorId, color);
233                }
234                
235
236                /*StructUtil.setELIgnoreWhenNull(sct,"borderColor", borderColor);
237                StructUtil.setELIgnoreWhenNull(sct,"fontColor", fontColor);
238                StructUtil.setELIgnoreWhenNull(sct,"highLightColor", highLightColor);
239                StructUtil.setELIgnoreWhenNull(sct,"normalColor", normalColor);
240                */
241                StructUtil.setELIgnoreWhenNull(sct,"colorId", colorId.getString());
242                StructUtil.setELIgnoreWhenNull(sct,KeyConstants._comment, dt.getComment());
243                StructUtil.setELIgnoreWhenNull(sct,KeyConstants._height, dt.getHeight());
244                StructUtil.setELIgnoreWhenNull(sct,KeyConstants._width, dt.getWidth());
245                StructUtil.setELIgnoreWhenNull(sct,KeyConstants._title, dt.getTitle());
246                
247                sct.setEL(KeyConstants._type, type.getString());
248                if(!StringUtil.isEmpty(dt.getId()))sct.setEL(KeyConstants._id, dt.getId());
249                        
250                if("ref".equals(dt.getType())){
251                        hasReference.setValue(true);
252                        sct.setEL(KeyConstants._ref, dt.getRef());
253                }
254                
255                
256                DumpRow[] drs = dt.getRows();
257                DumpRow dr;
258                Query qry=null;
259                DumpData[] items;
260                for(int r=0;r<drs.length;r++){
261                        dr=drs[r];
262                        items = dr.getItems();
263                        if(qry==null)qry=new QueryImpl(toColumns(items),drs.length,"data");
264                        for(int c=1;c<=items.length;c++){
265                                qry.setAtEL("data"+c, r+1, toCFML(items[c-1],object,hasReference,colors));
266                        }
267                        qry.setAtEL("highlight", r+1, new Double(dr.getHighlightType()));
268                        
269                }
270                if(qry!=null)sct.setEL(KeyConstants._data, qry);
271                return sct;
272        }
273
274        private static String[] toColumns(DumpData[] items) {
275                String[] columns=new String[items.length+1];
276                columns[0]="highlight";
277                for(int i=1;i<columns.length;i++){
278                        columns[i]="data"+i;
279                }
280                return columns;
281        }
282                
283        private static String toShortColor(String color) {
284                if(color!=null && color.length()==7 && color.startsWith("#")) {
285                        if(color.charAt(1)==color.charAt(2) && color.charAt(3)==color.charAt(4) && color.charAt(5)==color.charAt(6))
286                                return "#"+color.charAt(1)+color.charAt(3)+color.charAt(5);
287                        
288                        
289                } 
290                
291                
292                return color;
293        }
294}