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.io.IOException;
022import java.io.InputStream;
023import java.io.PrintStream;
024import java.io.PrintWriter;
025import java.io.StringWriter;
026import java.lang.reflect.InvocationTargetException;
027import java.util.Iterator;
028import java.util.LinkedList;
029
030import lucee.commons.io.CharsetUtil;
031import lucee.commons.io.IOUtil;
032import lucee.commons.io.res.Resource;
033import lucee.commons.io.res.util.ResourceUtil;
034import lucee.commons.lang.ClassUtil;
035import lucee.commons.lang.ExceptionUtil;
036import lucee.commons.lang.MappingUtil;
037import lucee.commons.lang.StringUtil;
038import lucee.runtime.Info;
039import lucee.runtime.PageContext;
040import lucee.runtime.PageContextImpl;
041import lucee.runtime.PageSource;
042import lucee.runtime.PageSourceImpl;
043import lucee.runtime.config.Config;
044import lucee.runtime.config.ConfigWeb;
045import lucee.runtime.dump.DumpData;
046import lucee.runtime.dump.DumpProperties;
047import lucee.runtime.dump.DumpTable;
048import lucee.runtime.dump.SimpleDumpData;
049import lucee.runtime.engine.ThreadLocalPageContext;
050import lucee.runtime.err.ErrorPage;
051import lucee.runtime.op.Caster;
052import lucee.runtime.type.Array;
053import lucee.runtime.type.ArrayImpl;
054import lucee.runtime.type.Collection;
055import lucee.runtime.type.Collection.Key;
056import lucee.runtime.type.Struct;
057import lucee.runtime.type.StructImpl;
058import lucee.runtime.type.dt.DateTimeImpl;
059import lucee.runtime.type.util.KeyConstants;
060import lucee.runtime.type.util.ListUtil;
061import lucee.runtime.writer.CFMLWriter;
062import lucee.transformer.bytecode.util.SourceNameClassVisitor.SourceInfo;
063
064/**
065 * Lucee Runtime Page Exception, all runtime Exception are sub classes of this class
066 */
067public abstract class PageExceptionImpl extends PageException {
068
069        private static final long serialVersionUID = -5816929795661373219L;
070        
071        
072        
073        
074        
075        private Array tagContext=new ArrayImpl();
076        private Struct additional=new StructImpl(Struct.TYPE_LINKED);
077        /**
078         * Field <code>detail</code>
079         */
080        protected String detail="";
081        //private Throwable rootCause;
082        private int tracePointer;
083        private String errorCode="0";
084        private String extendedInfo=null;
085
086        private String type;
087        private String customType;
088        private boolean isInitTagContext=false;
089        private LinkedList<PageSource> sources=new LinkedList<PageSource>();
090        private String varName;
091
092
093
094
095
096        private boolean exposeMessage;
097
098
099        /**
100         * Class Constructor
101         * @param message Exception Message
102         * @param type Type as String
103         */
104        public PageExceptionImpl(String message,String type) {
105                this(message,type,null);
106        }
107        /**
108         * Class Constructor
109         * @param message Exception Message
110         * @param type Type as String
111         * @param customType CUstom Type as String
112         */
113        public PageExceptionImpl(String message,String type, String customType) {
114                super(message==null?"":message);
115                //rootCause=this;
116                this.type=type.toLowerCase().trim();
117                this.customType=customType;
118        //setAdditional("customType",getCustomTypeAsString());
119        }
120        
121        /**
122         * Class Constructor
123         * @param e exception
124         * @param type Type as String
125         */
126        public PageExceptionImpl(Throwable e,String type) {
127                this(e,type,null);
128        }
129        
130        /**
131         * Class Constructor
132         * @param e exception
133         * @param type Type as String
134         * @param customType CUstom Type as String
135         */
136        public PageExceptionImpl(Throwable e,String type, String customType) {
137                super(StringUtil.isEmpty(e.getMessage(),true)?e.getClass().getName():e.getMessage());
138                if(e instanceof InvocationTargetException)e=((InvocationTargetException)e).getTargetException();
139        
140        //this.i
141        initCause(e);
142        //this.setStackTrace(e.getStackTrace());
143        
144                if(e instanceof IPageException) {
145            IPageException pe=(IPageException)e;
146                        this.additional=pe.getAdditional();
147                        this.setDetail(pe.getDetail());
148                        this.setErrorCode(pe.getErrorCode());
149                        this.setExtendedInfo(pe.getExtendedInfo());
150                }
151                
152                //else if(e.getCause()!=null)rootCause=e.getCause();
153                //else rootCause=e;
154
155                this.type=type.trim();
156                this.customType=(customType==null)?this.type:customType;
157        }
158    
159    @Override
160        public String getDetail() { 
161                if(detail==null || detail.equals(getMessage()))return "";
162                return detail; 
163        }
164        
165        @Override
166        public String getErrorCode() { return errorCode==null?"":errorCode; }
167        
168        @Override
169        public String getExtendedInfo() { return extendedInfo==null?"":extendedInfo; }
170        
171        @Override
172        public void setDetail(String detail) {
173                this.detail=detail;
174        }
175        @Override
176        public void setErrorCode(String errorCode) {
177                this.errorCode=errorCode;
178        }
179        @Override
180        public void setExtendedInfo(String extendedInfo) {
181                this.extendedInfo=extendedInfo;
182        }
183        
184        public final Struct getCatchBlock() {
185                return getCatchBlock(ThreadLocalPageContext.getConfig());
186        }
187        
188        @Override
189        public final Struct getCatchBlock(PageContext pc) {
190                return getCatchBlock(ThreadLocalPageContext.getConfig(pc));
191        }
192        
193        @Override
194        public CatchBlock getCatchBlock(Config config) {
195                return new CatchBlockImpl(this);
196        }
197        
198        public Array getTagContext(Config config) {
199                if(isInitTagContext) return tagContext;
200                _getTagContext( config,tagContext,getStackTraceElements(this),sources);
201                isInitTagContext=true;
202                return tagContext;
203        }
204        
205
206        public static Array getTagContext(Config config,StackTraceElement[] traces) {
207                Array tagContext=new ArrayImpl();
208                _getTagContext( config,tagContext,traces,new LinkedList<PageSource>());
209                return tagContext;
210        }
211
212        private static void _getTagContext(Config config, Array tagContext, StackTraceElement[] traces, 
213                        LinkedList<PageSource> sources) {
214                //StackTraceElement[] traces = getStackTraceElements(t);
215                int line=0;
216                String template="",tlast;
217                Struct item;
218                StackTraceElement trace=null;
219                int index=-1;
220                PageSource ps;
221                
222                PageContextImpl pc=null;
223                if(config instanceof ConfigWeb) 
224                        pc = (PageContextImpl) ThreadLocalPageContext.get();
225                
226                
227                for(int i=0;i<traces.length;i++) {
228                        trace=traces[i];
229                        tlast=template;
230                        template=trace.getFileName();
231                        
232                        if(trace.getLineNumber()<=0 || template==null || ResourceUtil.getExtension(template,"").equals("java")) continue;
233                        // content
234                        if(!StringUtil.emptyIfNull(tlast).equals(template))index++;
235                        
236                        String[] content=null;
237                        String dspPath=template;
238                        try {
239                                
240                                
241                                Resource res = config.getResource(template);
242                                if(!res.exists()) {
243                                        PageSource _ps = pc==null?null:pc.getPageSource(template);
244                                        res=_ps==null?null:_ps.getPhyscalFile();
245                                        if(res==null || !res.exists()) {
246                                                res=config.getResource(_ps.getDisplayPath());
247                                                if(res!=null && res.exists()) dspPath=res.getAbsolutePath();
248                                        }
249                                        else dspPath=res.getAbsolutePath();
250                                }
251                                else dspPath=res.getAbsolutePath();
252                                
253                                // template is absolute, so this is not necessary if(!res.exists()) res = ResourceUtil.toResourceNotExisting(ThreadLocalPageContext.get(), template,true,true); 
254                                
255                                // class was not build on the local filesystem
256                                if(!res.exists()) {
257                                        // try to make a match by class name
258                                        
259                                        SourceInfo si=pc!=null?MappingUtil.getMatch(pc,trace):MappingUtil.getMatch(config,trace);
260                                        if(si!=null && si.relativePath!=null) {
261                                                dspPath=si.relativePath;
262                                                res = ResourceUtil.toResourceNotExisting(ThreadLocalPageContext.get(), si.relativePath,true,true); 
263                                                if(!res.exists()) {
264                                                        PageSource _ps = PageSourceImpl.best(config.getPageSources(ThreadLocalPageContext.get(), null, si.relativePath, false, false, true));
265                                                        if(_ps!=null && _ps.exists()) {
266                                                                res=_ps.getResource();
267                                                        }
268                                                }
269                                        }
270                                }
271                                 
272                                if(res.exists()) {
273                                        InputStream is = res.getInputStream();
274                                        if (ClassUtil.isBytecode(is)) {
275                                                content = new String[] {}; //empty code array to show ??
276                                        }
277                                        else
278                                                content=IOUtil.toStringArray(IOUtil.getReader(res,CharsetUtil.toCharset(config.getTemplateCharset())));
279                                        IOUtil.closeEL(is);
280                                }
281                                else {
282                                        if(sources.size()>index)ps = sources.get(index);
283                                        else ps=null;
284
285                                        if(ps!=null && trace.getClassName().equals(ps.getFullClassName())) {
286                                                if(ps.physcalExists())
287                                                        content=IOUtil.toStringArray(IOUtil.getReader(ps.getPhyscalFile(), CharsetUtil.toCharset(config.getTemplateCharset())));
288                                                template=ps.getDisplayPath();
289                                        }
290                                }       
291                        } 
292                        catch (Throwable th) {
293                                ExceptionUtil.rethrowIfNecessary(th);
294                                th.printStackTrace();
295                        }
296                        
297                        // check last
298                        if(tagContext.size()>0){
299                                try {
300                                        Struct last=(Struct) tagContext.getE(tagContext.size());
301                                        if(last.get(KeyConstants._Raw_Trace).equals(trace.toString()))continue;
302                                } 
303                                catch (Exception e) {
304                                        //e.printStackTrace();
305                                }
306                        }
307                        
308                        item=new StructImpl();
309                        line=trace.getLineNumber();
310                        item.setEL(KeyConstants._template,dspPath);
311                        item.setEL(KeyConstants._line,new Double(line));
312                        item.setEL(KeyConstants._id,"??");
313                        item.setEL(KeyConstants._Raw_Trace,trace.toString());
314                        item.setEL(KeyConstants._type,"cfml");
315                        item.setEL(KeyConstants._column,new Double(0));
316                        if(content!=null) {
317                                if(content.length > 0) {
318                                        item.setEL(KeyConstants._codePrintHTML,getCodePrint(content,line,true));
319                                        item.setEL(KeyConstants._codePrintPlain,getCodePrint(content,line,false));
320                                } else {
321                                        item.setEL(KeyConstants._codePrintHTML,"??");
322                                        item.setEL(KeyConstants._codePrintPlain,"??");
323                                }
324                        }
325                        else {
326                                item.setEL(KeyConstants._codePrintHTML,"");
327                                item.setEL(KeyConstants._codePrintPlain,"");
328                        }
329                        // FUTURE id 
330                        tagContext.appendEL(item);
331                }
332        }
333        
334        
335        
336        public int getPageDeep() {
337                StackTraceElement[] traces = getStackTraceElements(this);
338                
339                String template="",tlast;
340                StackTraceElement trace=null;
341                int index=0;
342                for(int i=0;i<traces.length;i++) {
343                        trace=traces[i];
344                        tlast=template;
345                        template=trace.getFileName();
346                        if(trace.getLineNumber()<=0 || template==null || ResourceUtil.getExtension(template,"").equals("java")) continue;
347                        if(!StringUtil.emptyIfNull(tlast).equals(template))index++;
348                        
349                }
350                return index;
351        }
352        
353        
354        @Override
355        public Struct getErrorBlock(PageContext pc,ErrorPage ep) {
356                Struct struct=new StructImpl();
357
358                struct.setEL("browser",pc.cgiScope().get("HTTP_USER_AGENT",""));
359                struct.setEL("datetime",new DateTimeImpl(pc));
360                struct.setEL("diagnostics",getMessage()+' '+getDetail()+"<br>The error occurred on line "+getLine(pc.getConfig())+" in file "+getFile(pc.getConfig())+".");
361                struct.setEL("GeneratedContent",getGeneratedContent(pc));
362                struct.setEL("HTTPReferer",pc.cgiScope().get("HTTP_REFERER",""));
363                struct.setEL("mailto",ep.getMailto());
364                struct.setEL(KeyConstants._message,getMessage());
365                struct.setEL("QueryString",StringUtil.emptyIfNull(pc. getHttpServletRequest().getQueryString()));
366                struct.setEL("RemoteAddress",pc.cgiScope().get("REMOTE_ADDR",""));
367                struct.setEL("RootCause",getCatchBlock(pc));
368                struct.setEL("StackTrace",getStackTraceAsString());
369                struct.setEL(KeyConstants._template,pc. getHttpServletRequest().getServletPath());
370                
371                struct.setEL(KeyConstants._Detail,getDetail());
372                struct.setEL("ErrorCode",getErrorCode());
373                struct.setEL("ExtendedInfo",getExtendedInfo());
374                struct.setEL(KeyConstants._type,getTypeAsString());
375                struct.setEL("TagContext",getTagContext(pc.getConfig()));
376                struct.setEL("additional",additional);
377                        // TODO RootCause,StackTrace
378                
379                return struct;
380        }
381        
382        private String getGeneratedContent(PageContext pc){
383                PageContextImpl pci=(PageContextImpl)pc;
384                CFMLWriter ro=pci.getRootOut();
385                String gc=ro.toString();
386                try{
387                        ro.clearBuffer();
388                }
389                catch(IOException ioe){}
390                if(gc==null) return "";
391                return gc;
392        }
393        
394        
395        /**
396         * @return return the file where the failure occurred
397         */
398        private String getFile(Config config) {
399        if(getTagContext(config).size()==0) return "";
400        
401        Struct sct=(Struct) getTagContext(config).get(1,null);
402        return Caster.toString(sct.get(KeyConstants._template,""),"");
403    }
404    
405        public String getLine(Config config) {
406        if(getTagContext(config).size()==0) return "";
407        
408        Struct sct=(Struct) getTagContext(config).get(1,null);
409        return Caster.toString(sct.get(KeyConstants._line,""),"");
410    }
411    
412        @Override
413        public void addContext(PageSource pr, int line, int column, StackTraceElement element) {
414                if(line==-187) {
415                        sources.add(pr);
416                        return;
417                }
418        
419                Struct struct=new StructImpl();
420        //print.out(pr.getDisplayPath());
421                try {
422                        String[] content=pr.getSource();
423                        struct.set(KeyConstants._template,pr.getDisplayPath());
424                        struct.set(KeyConstants._line,new Double(line));
425                        struct.set(KeyConstants._id,"??");
426                        struct.set(KeyConstants._Raw_Trace,(element!=null)?element.toString():"");
427                        struct.set(KeyConstants._Type,"cfml");
428                        struct.set(KeyConstants._column,new Double(column));
429                        if(content!=null){
430                                struct.set(KeyConstants._codePrintHTML,getCodePrint(content,line,true));
431                                struct.set(KeyConstants._codePrintPlain,getCodePrint(content,line,false));
432                        }
433                        tagContext.append(struct);
434                } 
435                catch (Exception e) {}
436        }
437        
438        private static String getCodePrint(String[] content,int line, boolean asHTML ) {
439                StringBuilder sb=new StringBuilder();
440                // bad Line
441                for(int i=line-2;i<line+3;i++) {
442                        if(i>0 && i<=content.length) {
443                                if(asHTML && i==line)sb.append("<b>");
444                                if(asHTML)sb.append(i+": "+StringUtil.escapeHTML(content[i-1]));
445                                else sb.append(i+": "+(content[i-1]));
446                                if(asHTML && i==line)sb.append("</b>");
447                                if(asHTML)sb.append("<br>");
448                                sb.append('\n');
449                        }
450                }
451                return sb.toString();
452        }
453        
454        @Override
455        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
456                
457                //FFFFCF
458        DumpTable htmlBox = new DumpTable("exception","#ff9900","#FFCC00","#000000");
459                htmlBox.setTitle("Lucee ["+Info.getVersionAsString()+"] - Error ("+StringUtil.ucFirst(getTypeAsString())+")");
460                
461                
462                // Message
463                htmlBox.appendRow(1,new SimpleDumpData("Message"),new SimpleDumpData(getMessage()));
464                
465                // Detail
466                String detail=getDetail();
467                if(!StringUtil.isEmpty(detail,true))
468                        htmlBox.appendRow(1,new SimpleDumpData("Detail"),new SimpleDumpData(detail));
469                
470                // additional
471                Iterator<Key> it = additional.keyIterator();
472                Collection.Key k;
473                while(it.hasNext()) {
474                        k=it.next();
475                        htmlBox.appendRow(1,new SimpleDumpData(k.getString()),new SimpleDumpData(additional.get(k,"").toString()));
476                }
477                
478                Array tagContext = getTagContext(pageContext.getConfig());
479                // Context MUSTMUST
480                if(tagContext.size()>0) {
481                        //Collection.Key[] keys=tagContext.keys();
482                        Iterator<Object> vit = tagContext.valueIterator();
483                        //Entry<Key, Object> te;
484                        DumpTable context=new DumpTable("#ff9900","#FFCC00","#000000");
485                        //context.setTitle("The Error Occurred in");
486                        //context.appendRow(0,new SimpleDumpData("The Error Occurred in"));
487                        context.appendRow(7,
488                                        new SimpleDumpData(""),
489                                        new SimpleDumpData("template"),
490                                        new SimpleDumpData("line"));
491                        try {
492                                boolean first=true;
493                                while(vit.hasNext()) {
494                                        Struct struct=(Struct)vit.next();
495                                        context.appendRow(1,
496                                                        new SimpleDumpData(first?"called from ":"occurred in"),
497                                                        new SimpleDumpData(struct.get(KeyConstants._template,"")+""),
498                                                        new SimpleDumpData(Caster.toString(struct.get(KeyConstants._line,null))));
499                                        first=false;
500                                }
501                                htmlBox.appendRow(1,new SimpleDumpData("Context"),context);
502                                
503                                
504                                // Code
505                                String strCode=((Struct)tagContext.get(1,null)).get(KeyConstants._codePrintPlain,"").toString();
506                                String[] arrCode = ListUtil.listToStringArray(strCode, '\n');
507                                arrCode=ListUtil.trim(arrCode);
508                                DumpTable code=new DumpTable("#ff9900","#FFCC00","#000000");
509                                
510                                for(int i=0;i<arrCode.length;i++) {
511                                        code.appendRow(i==2?1:0,new SimpleDumpData(arrCode[i]));
512                                }
513                                htmlBox.appendRow(1,new SimpleDumpData("Code"),code);
514
515                        } 
516            catch (PageException e) {}
517                }
518                
519                
520                // Java Stacktrace
521                String strST=getStackTraceAsString();
522                String[] arrST = ListUtil.listToStringArray(strST, '\n');
523                arrST=ListUtil.trim(arrST);
524                DumpTable st=new DumpTable("#ff9900","#FFCC00","#000000");
525                
526                for(int i=0;i<arrST.length;i++) {
527                        st.appendRow(i==0?1:0,new SimpleDumpData(arrST[i]));
528                }
529                htmlBox.appendRow(1,new SimpleDumpData("Java Stacktrace"),st);
530
531                return htmlBox; 
532        }       
533        
534        @Override
535public String getStackTraceAsString() {
536                return getStackTraceAsString(ThreadLocalPageContext.get());
537    }
538        
539        public String getStackTraceAsString(PageContext pc) {
540                
541        StringWriter sw=new StringWriter();
542            PrintWriter pw=new PrintWriter(sw);
543        printStackTrace(pw);
544        pw.flush();
545        return sw.toString();
546    }
547
548    @Override
549    public void printStackTrace() {
550        printStackTrace(System.err);
551    }
552    
553    public void printStackTrace(PageContext pc) {
554        printStackTrace(System.err,pc);
555    }
556    
557    @Override
558    public void printStackTrace(PrintStream s) {
559        printStackTrace(s, ThreadLocalPageContext.get());
560    }
561    
562    public void printStackTrace(PrintStream s,PageContext pc) {
563        StackTraceElement[] traces = getStackTraceElements(this);
564        StackTraceElement trace;
565        
566        s.println(getMessage());
567        for(int i=0;i<traces.length;i++){
568            trace=traces[i];
569            s.println("\tat "+toString(pc,trace)+":"+trace.getLineNumber());
570        }
571    }
572    
573
574    @Override
575    public void printStackTrace(PrintWriter s) {
576        printStackTrace(s, ThreadLocalPageContext.get());
577    }
578    
579    public void printStackTrace(PrintWriter s,PageContext pc) {
580        StackTraceElement[] traces = getStackTraceElements(this);
581        StackTraceElement trace;
582        
583        s.println(getMessage());
584        for(int i=0;i<traces.length;i++){
585            trace=traces[i];
586            s.println("\tat "+toString(pc,trace)+":"+trace.getLineNumber());
587        }
588    }
589    
590
591    public static String toString(PageContext pc, StackTraceElement trace) {
592        Config config = ThreadLocalPageContext.getConfig(pc);
593        String path=null;
594                
595        if(trace.getFileName()==null || trace.getFileName().endsWith(".java"))
596                return trace.toString();
597        if(config!=null) {
598                Resource res = config.getResource(
599                                trace.getFileName());
600                if(res.exists()) path=trace.getFileName();
601                
602                // get path from source
603                if(path==null){
604                                SourceInfo si=MappingUtil.getMatch(pc,config,trace);
605                                if(si!=null) {
606                                        String abs=si.absolutePath(pc);
607                                        if(!StringUtil.isEmpty(abs)) {
608                                                Resource r = config.getResource(abs);
609                                                if(r.exists()) path=abs;
610                                        }
611                                        if(path==null && si.relativePath!=null) path=si.relativePath;
612                                }
613                                if(path==null) path=trace.getFileName();
614                }
615        }
616                return trace.getClassName() + "." + trace.getMethodName() +
617        (trace.isNativeMethod() ? "(Native Method)" :
618         (path != null && trace.getLineNumber() >= 0 ?
619          "(" + path + ":" + trace.getLineNumber() + ")" :
620          (path != null ?  "("+path+")" : "(Unknown Source)")));
621
622        }
623    
624    
625        private static StackTraceElement[] getStackTraceElements(Throwable t) {
626        StackTraceElement[] st=getStackTraceElements(t,true);
627        if(st==null) st= getStackTraceElements(t,false);
628        return st;
629    }
630    
631    private static StackTraceElement[] getStackTraceElements(Throwable t, boolean onlyWithCML) {
632        StackTraceElement[] st;
633        Throwable cause=t.getCause();
634        if(cause!=null){
635                st = getStackTraceElements(cause,onlyWithCML);
636                if(st!=null) return st;
637        }
638        
639        st=t.getStackTrace();
640        if(!onlyWithCML || hasCFMLinStacktrace(st)){
641                return st;
642        }
643        return null;
644        }
645    
646
647    private static boolean hasCFMLinStacktrace(StackTraceElement[] traces) {
648                for(int i=0;i<traces.length;i++) {
649                        if(traces[i].getFileName()!=null && !traces[i].getFileName().endsWith(".java")) return true;
650                }
651                return false;
652        }
653    /*ths code has produced duplettes
654     * private static void fillStackTraceElements(ArrayList<StackTraceElement> causes, Throwable t) {
655                if(t==null) return;
656                fillStackTraceElements(causes, t.getCause());
657                StackTraceElement[] traces = t.getStackTrace();
658                for(int i=0;i<traces.length;i++) {
659                        //if(causes.contains(traces[i]))
660                        causes.add(traces[i]);
661                }
662        }*/
663    
664        /**
665         * set a additional key value
666         * @param key
667         * @param value
668         */
669        public void setAdditional(Collection.Key key, Object value) {
670                additional.setEL(key,StringUtil.toStringEmptyIfNull(value));
671        }
672        
673        
674        @Override
675        public Throwable getRootCause() {
676        Throwable cause=this; 
677        Throwable temp; 
678        
679        while((temp=cause.getCause())!=null)cause=temp;
680        return cause;
681        }
682
683        @Override
684        public int getTracePointer() {
685                return tracePointer;
686        }
687        @Override
688        public void setTracePointer(int tracePointer) {
689                this.tracePointer = tracePointer;
690        }
691        
692        @Override
693    public boolean typeEqual(String type) {
694        if(type==null) return true;
695        type=StringUtil.toUpperCase(type);
696        // ANY
697        if(type.equals("ANY")) return true;// MUST check
698        // Type Compare
699        if(getTypeAsString().equalsIgnoreCase(type)) return true;
700        return getClass().getName().equalsIgnoreCase(type);
701    }
702    
703    @Override
704        public String getTypeAsString() {
705                return type;
706        }
707    
708
709        public String getType() { // for compatibility to ACF
710                return type;
711        }
712        
713        @Override
714        public String getCustomTypeAsString() {
715                return customType==null?type:customType;
716        }
717    
718    @Override
719        public Struct getAdditional() {
720        return additional;
721    }public Struct getAddional() {
722        return additional;
723    }
724    
725    @Override
726    public StackTraceElement[] getStackTrace() {
727        return super.getStackTrace();
728    }
729        public void setExposeMessage(boolean exposeMessage) {
730                this.exposeMessage=exposeMessage;
731        }
732        public boolean getExposeMessage() {
733                return exposeMessage;
734        }
735    
736    /*public static void printStackTrace(PrintWriter s,Throwable t) {
737        
738        //while((temp=cause.getCause())!=null)cause=temp;
739        
740        StackTraceElement[] traces = t.getStackTrace();
741        StackTraceElement trace;
742        
743        s.println(t.getMessage());
744        for(int i=0;i<traces.length;i++){
745            trace=traces[i];
746            s.println("\tat "+trace+":"+trace.getLineNumber());
747        }
748        t=t.getCause();
749        if(t!=null)printStackTrace(s,t);
750    }*/
751    
752    
753    
754}