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.exp;
020
021import java.lang.reflect.Method;
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Set;
026
027import lucee.commons.lang.CFTypes;
028import lucee.commons.lang.StringUtil;
029import lucee.runtime.PageContext;
030import lucee.runtime.PageContextImpl;
031import lucee.runtime.dump.DumpData;
032import lucee.runtime.dump.DumpProperties;
033import lucee.runtime.engine.ThreadLocalPageContext;
034import lucee.runtime.op.Castable;
035import lucee.runtime.op.Caster;
036import lucee.runtime.op.Decision;
037import lucee.runtime.reflection.Reflector;
038import lucee.runtime.reflection.pairs.MethodInstance;
039import lucee.runtime.type.Collection;
040import lucee.runtime.type.KeyImpl;
041import lucee.runtime.type.Objects;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.StructImpl;
044import lucee.runtime.type.it.EntryIterator;
045import lucee.runtime.type.it.KeyIterator;
046import lucee.runtime.type.it.StringIterator;
047import lucee.runtime.type.it.ValueIterator;
048import lucee.runtime.type.util.ArrayUtil;
049import lucee.runtime.type.util.KeyConstants;
050import lucee.runtime.type.util.MemberUtil;
051import lucee.runtime.type.util.StructUtil;
052
053public class CatchBlockImpl extends StructImpl implements CatchBlock,Castable,Objects{
054        
055        private static final long serialVersionUID = -3680961614605720352L;
056        
057        public static final Key ERROR_CODE = KeyImpl.intern("ErrorCode");
058        public static final Key EXTENDEDINFO = KeyImpl.intern("ExtendedInfo");
059        public static final Key EXTENDED_INFO = KeyImpl.intern("Extended_Info");
060        public static final Key TAG_CONTEXT = KeyImpl.intern("TagContext");
061        public static final Key STACK_TRACE = KeyImpl.intern("StackTrace");
062        public static final Key ADDITIONAL = KeyImpl.intern("additional");
063        private static final Object NULL = new Object();
064        
065        private PageExceptionImpl exception;
066        
067
068        public CatchBlockImpl(PageExceptionImpl pe) {
069                this.exception=pe;
070
071                setEL(KeyConstants._Message, new SpecialItem(pe, KeyConstants._Message));
072                setEL(KeyConstants._Detail, new SpecialItem(pe, KeyConstants._Detail));
073                setEL(ERROR_CODE, new SpecialItem(pe, ERROR_CODE));
074                setEL(EXTENDEDINFO, new SpecialItem(pe, EXTENDEDINFO));
075                setEL(EXTENDED_INFO, new SpecialItem(pe, EXTENDED_INFO));
076                setEL(ADDITIONAL, new SpecialItem(pe, ADDITIONAL));
077                setEL(TAG_CONTEXT, new SpecialItem(pe, TAG_CONTEXT));
078                setEL(KeyConstants._type, new SpecialItem(pe, KeyConstants._type));
079                setEL(STACK_TRACE, new SpecialItem(pe, STACK_TRACE));
080                
081                
082                if(pe instanceof NativeException){
083                        Throwable throwable = ((NativeException)pe).getRootCause();
084                        Method[] mGetters = Reflector.getGetters(throwable.getClass());
085                        Method getter;
086                        Collection.Key key;
087                        if(!ArrayUtil.isEmpty(mGetters)){
088                                for(int i=0;i<mGetters.length;i++){
089                                        getter=mGetters[i];
090                                        if(getter.getDeclaringClass()==Throwable.class) {
091                                                continue;
092                                        }
093                                        key=KeyImpl.init(Reflector.removeGetterPrefix(getter.getName()));
094                                        if(STACK_TRACE.equalsIgnoreCase(key)) continue;
095                                        setEL(key,new Pair(throwable,key, getter,false));
096                                }
097                        }
098                }
099        }
100
101        
102        class SpecialItem {
103                private PageExceptionImpl pe;
104                private Key key;
105                
106                public SpecialItem(PageExceptionImpl pe, Key key) {
107                        this.pe=pe;
108                        this.key=key;
109                }
110                
111                public Object get() {
112                        if(key==KeyConstants._Message) return StringUtil.emptyIfNull(pe.getMessage());
113                        if(key==KeyConstants._Detail) return StringUtil.emptyIfNull(pe.getDetail());
114                        if(key==ERROR_CODE) return StringUtil.emptyIfNull(pe.getErrorCode());
115                        if(key==EXTENDEDINFO) return StringUtil.emptyIfNull(pe.getExtendedInfo());
116                        if(key==EXTENDED_INFO) return StringUtil.emptyIfNull(pe.getExtendedInfo());
117                        if(key==KeyConstants._type) return StringUtil.emptyIfNull(pe.getTypeAsString());
118                        if(key==STACK_TRACE) return StringUtil.emptyIfNull(pe.getStackTraceAsString());
119                        if(key==ADDITIONAL) return pe.getAddional();
120                        if(key==TAG_CONTEXT) return pe.getTagContext(ThreadLocalPageContext.getConfig());
121                        return null;
122                }
123                
124                public void set(Object o){
125                        try {
126                                if(!(o instanceof Pair)) {
127                                        if(key==KeyConstants._Detail) {
128                                                pe.setDetail(Caster.toString(o));
129                                                return;
130                                        }
131                                        else if(key==ERROR_CODE) {
132                                                pe.setErrorCode(Caster.toString(o));
133                                                return;
134                                        }
135                                        else if(key==EXTENDEDINFO || key==EXTENDED_INFO) {
136                                                pe.setExtendedInfo(Caster.toString(o));
137                                                return;
138                                        }
139                                        else if(key==STACK_TRACE) {
140                                                if(o instanceof StackTraceElement[]){
141                                                        pe.setStackTrace((StackTraceElement[])o);
142                                                        return;
143                                                }
144                                                else if(Decision.isCastableToArray(o)) {
145                                                        Object[] arr = Caster.toNativeArray(o);
146                                                        StackTraceElement[] elements=new StackTraceElement[arr.length];
147                                                        for(int i=0;i<arr.length;i++) {
148                                                                if(arr[i] instanceof StackTraceElement)
149                                                                        elements[i]=(StackTraceElement) arr[i];
150                                                                else
151                                                                        throw new CasterException(o, StackTraceElement[].class);
152                                                        }
153                                                        pe.setStackTrace(elements);
154                                                        return;
155                                                        
156                                                }
157                                        }
158                                }
159                        }
160                        catch(PageException pe){}
161                        
162                        superSetEL(key,o);
163                        
164                        
165                }
166                public Object remove(){
167                        Object rtn=null;
168                        if(key==KeyConstants._Detail) {
169                                rtn=pe.getDetail();
170                                pe.setDetail("");
171                        }
172                        else if(key==ERROR_CODE)  {
173                                rtn=pe.getErrorCode();
174                                pe.setErrorCode("0");
175                        }
176                        else if(key==EXTENDEDINFO || key==EXTENDED_INFO)  {
177                                rtn=pe.getExtendedInfo();
178                                pe.setExtendedInfo(null);
179                        }
180                        return rtn;
181                        
182                }
183        }
184        
185        
186        /**
187         * @return the pe
188         */
189        public PageException getPageException() {
190                return exception;
191        }
192
193        @Override
194        public String castToString() throws ExpressionException {
195                return castToString(null);
196        }
197        
198        @Override
199        public String castToString(String defaultValue) {
200                return exception.getClass().getName();
201        }
202
203        @Override
204        public boolean containsValue(Object value) {
205                Key[] keys = keys();
206                for(int i=0;i<keys.length;i++){
207                        if(get(keys[i],null)==value) return true;
208                }
209                return false;
210        }
211
212        @Override
213        public Collection duplicate(boolean deepCopy) {
214                Struct sct=new StructImpl();
215                StructUtil.copy(this, sct, true);
216                return sct;
217        }
218
219        @Override
220        public Set entrySet() {
221                return StructUtil.entrySet(this);
222        }
223        
224        public void print(PageContext pc){
225                ((PageContextImpl)pc).handlePageException(exception);
226                
227        }
228
229        @Override
230        public Object get(Key key, Object defaultValue) {
231                Object value = super.get(key,defaultValue);
232                if(value instanceof SpecialItem) {
233                        return ((SpecialItem)value).get();
234                }
235                else if(value instanceof Pair) {
236                        Pair pair=(Pair) value;
237                        try {
238                                Object res = pair.getter.invoke(pair.throwable, new Object[]{});
239                                if(pair.doEmptyStringWhenNull && res==null) return "";
240                                return res;
241                        } 
242                        catch (Exception e) {
243                                return defaultValue;
244                        }
245                }
246                return value;
247        }
248
249        @Override
250        public Object set(Key key, Object value) throws PageException {
251                Object curr = super.get(key,null);
252                if(curr instanceof SpecialItem){
253                        ((SpecialItem)curr).set(value);
254                        return value;
255                }
256                else if(curr instanceof Pair){
257                        Pair pair=(Pair) curr;
258                        MethodInstance setter = Reflector.getSetter(pair.throwable, pair.name.getString(), value,null);
259                        if(setter!=null){
260                                try {
261                                        setter.invoke(pair.throwable);
262                                        return value;
263                                } catch (Exception e) {
264                                        throw Caster.toPageException(e);
265                                }
266                        }
267                }
268                
269                return super.set(key, value);
270        }
271
272        @Override
273        public Object setEL(Key key, Object value) {
274                Object curr = super.get(key,null);
275                if(curr instanceof SpecialItem){
276                        ((SpecialItem)curr).set(value);
277                        return value;
278                }
279                else if(curr instanceof Pair){
280                        Pair pair=(Pair) curr;
281                        MethodInstance setter = Reflector.getSetter(pair.throwable, pair.name.getString(), value,null);
282                        if(setter!=null){
283                                try {
284                                        setter.invoke(pair.throwable);
285                                } catch (Exception e) {}
286                                return value;
287                        }
288                }
289                return super.setEL(key, value);
290        }
291        
292        private Object superSetEL(Key key, Object value) {
293                return super.setEL(key, value);
294        }
295
296        @Override
297        public int size() {
298                return keys().length;
299        }
300
301        @Override
302        public Key[] keys() {
303                Key[] keys = super.keys();
304                List<Key> list=new ArrayList<Key>();
305                for(int i=0;i<keys.length;i++){
306                        if(get(keys[i], null)!=null)list.add(keys[i]);
307                }
308                return list.toArray(new Key[list.size()]);
309        }
310
311        @Override
312        public Object remove(Key key) throws PageException {
313                Object curr = super.get(key,null);
314                if(curr instanceof SpecialItem){
315                        return ((SpecialItem)curr).remove();
316                }
317                else if(curr instanceof Pair){
318                        Pair pair=(Pair) curr;
319                        MethodInstance setter = Reflector.getSetter(pair.throwable, pair.name.getString(), null,null);
320                        if(setter!=null){
321                                try {
322                                        Object before = pair.getter.invoke(pair.throwable, new Object[0]);
323                                        setter.invoke(pair.throwable);
324                                        return before;
325                                } catch (Exception e) {
326                                        throw Caster.toPageException(e);
327                                }
328                        }
329                }       
330                return super.remove(key);
331        }
332
333        @Override
334        public Iterator<Collection.Key> keyIterator() {
335                return new KeyIterator(keys());
336        }
337    
338    @Override
339        public Iterator<String> keysAsStringIterator() {
340        return new StringIterator(keys());
341    }
342
343        @Override
344        public Iterator<Entry<Key, Object>> entryIterator() {
345                return new EntryIterator(this, keys());
346        }
347
348        @Override
349        public Iterator<Object> valueIterator() {
350                return new ValueIterator(this, keys());
351        }
352
353        @Override
354        public java.util.Collection values() {
355                return StructUtil.values(this);
356        }
357
358        @Override
359        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
360                return StructUtil.toDumpTable(this,"Catch",pageContext,maxlevel,dp);
361        }
362
363
364
365        class Pair{
366                Throwable throwable;
367                Collection.Key name;
368                Method getter;
369                private boolean doEmptyStringWhenNull;
370                
371                public Pair(Throwable throwable,Key name, Method method,boolean doEmptyStringWhenNull) {
372                        this.throwable = throwable;
373                        this.name = name;
374                        this.getter = method;
375                        this.doEmptyStringWhenNull = doEmptyStringWhenNull;
376                }
377                public Pair(Throwable throwable,String name, Method method, boolean doEmptyStringWhenNull) {
378                        this(throwable, KeyImpl.init(name), method,doEmptyStringWhenNull);
379                }
380                
381                public String toString(){
382                        try {
383                                return Caster.toString(getter.invoke(throwable, new Object[]{}));
384                        } catch (Exception e) {
385                                throw new PageRuntimeException(Caster.toPageException(e));
386                        }
387                }
388        }
389
390        public Object call(PageContext pc, String methodName, Object[] arguments) throws PageException {
391                Object obj=exception;
392                if(exception instanceof NativeException) obj=((NativeException)exception).getRootCause();
393                if("dump".equalsIgnoreCase(methodName)){
394                        print(pc);
395                        return null;
396                }
397                
398                return MemberUtil.call(pc, this, KeyImpl.init(methodName), arguments, CFTypes.TYPE_STRUCT, "struct");
399                
400                /*try{
401                        return Reflector.callMethod(obj, methodName, arguments);
402                }
403                catch(PageException e){
404                        return Reflector.callMethod(exception, methodName, arguments);
405                }*/
406        }
407
408        
409        
410        
411        public Object callWithNamedValues(PageContext pc, String methodName,Struct args) throws PageException {
412                throw new ApplicationException("named arguments not supported");
413        }
414
415        public Object callWithNamedValues(PageContext pc, Key methodName,Struct args) throws PageException {
416                throw new ApplicationException("named arguments not supported");
417        }
418        public boolean isInitalized() {
419                return true;
420        }
421
422        public Object set(PageContext pc, String propertyName, Object value) throws PageException {
423                return set(propertyName, value);
424        }
425
426        public Object set(PageContext pc, Key propertyName, Object value)throws PageException {
427                return set(propertyName, value);
428        }
429
430        public Object setEL(PageContext pc, String propertyName, Object value) {
431                return setEL(propertyName, value);
432        }
433
434        public Object setEL(PageContext pc, Key propertyName, Object value) {
435                return setEL(propertyName, value);
436        }
437        public Object get(Key key) throws PageException {
438                Object res = get(key,NULL);
439                if(res!=NULL) return res;
440                throw StructImpl.invalidKey(null,this, key);
441        }
442        public Object get(PageContext pc, String key, Object defaultValue) {
443                return get(key,defaultValue);
444        }
445
446        public Object get(PageContext pc, Key key, Object defaultValue) {
447                return get(key,defaultValue);
448        }
449
450        public Object get(PageContext pc, String key) throws PageException {
451                return get(key);
452        }
453
454        public Object get(PageContext pc, Key key) throws PageException {
455                return get(key);
456        }
457        public Object call(PageContext pc, Key methodName, Object[] arguments) throws PageException {
458                return call(pc, methodName.getString(), arguments);
459        }
460        /*public Object remove (String key) throws PageException {
461                return remove(KeyImpl.init(key));
462        }*/
463        public Object removeEL(Key key) {
464                try {
465                        return remove(key);
466                } catch (PageException e) {
467                        return null;
468                }
469        }
470}