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.tag;
020
021import java.io.IOException;
022import java.util.Iterator;
023import java.util.Map.Entry;
024
025import javax.servlet.jsp.JspWriter;
026import javax.servlet.jsp.tagext.Tag;
027
028import lucee.commons.lang.ExceptionUtil;
029import lucee.commons.lang.StringUtil;
030import lucee.runtime.Component;
031import lucee.runtime.Mapping;
032import lucee.runtime.PageContext;
033import lucee.runtime.PageContextImpl;
034import lucee.runtime.PageSource;
035import lucee.runtime.component.ComponentLoader;
036import lucee.runtime.component.Member;
037import lucee.runtime.config.ConfigWebImpl;
038import lucee.runtime.customtag.CustomTagUtil;
039import lucee.runtime.customtag.InitFile;
040import lucee.runtime.engine.ThreadLocalPageContext;
041import lucee.runtime.exp.ApplicationException;
042import lucee.runtime.exp.CasterException;
043import lucee.runtime.exp.ExpressionException;
044import lucee.runtime.exp.PageException;
045import lucee.runtime.exp.PageRuntimeException;
046import lucee.runtime.exp.PageServletException;
047import lucee.runtime.exp.TemplateException;
048import lucee.runtime.ext.tag.AppendixTag;
049import lucee.runtime.ext.tag.BodyTagTryCatchFinallyImpl;
050import lucee.runtime.ext.tag.DynamicAttributes;
051import lucee.runtime.op.Caster;
052import lucee.runtime.op.Decision;
053import lucee.runtime.type.Collection;
054import lucee.runtime.type.Collection.Key;
055import lucee.runtime.type.KeyImpl;
056import lucee.runtime.type.Struct;
057import lucee.runtime.type.StructImpl;
058import lucee.runtime.type.scope.Caller;
059import lucee.runtime.type.scope.CallerImpl;
060import lucee.runtime.type.scope.Undefined;
061import lucee.runtime.type.scope.Variables;
062import lucee.runtime.type.scope.VariablesImpl;
063import lucee.runtime.type.util.ArrayUtil;
064import lucee.runtime.type.util.ComponentProUtil;
065import lucee.runtime.type.util.KeyConstants;
066import lucee.runtime.type.util.ListUtil;
067import lucee.runtime.type.util.Type;
068import lucee.runtime.util.QueryStack;
069import lucee.runtime.util.QueryStackImpl;
070import lucee.transformer.library.tag.TagLibTag;
071import lucee.transformer.library.tag.TagLibTagAttr;
072
073
074/**
075* Creates a CFML Custom Tag
076**/
077public class CFTag extends BodyTagTryCatchFinallyImpl implements DynamicAttributes,AppendixTag {
078
079        private static Collection.Key GENERATED_CONTENT=KeyImpl.intern("GENERATEDCONTENT");
080        private static Collection.Key EXECUTION_MODE=KeyImpl.intern("EXECUTIONMODE");      
081        private static Collection.Key EXECUTE_BODY=KeyImpl.intern("EXECUTEBODY");
082        private static Collection.Key PARENT=KeyImpl.intern("PARENT");
083        private static Collection.Key CFCATCH=KeyConstants._CFCATCH;
084        private static Collection.Key SOURCE=KeyImpl.intern("SOURCE");
085        
086        private static final Collection.Key ON_ERROR = KeyImpl.intern("onError");
087        private static final Collection.Key ON_FINALLY = KeyImpl.intern("onFinally");
088        private static final Collection.Key ON_START_TAG = KeyImpl.intern("onStartTag");
089        private static final Collection.Key ON_END_TAG = KeyImpl.intern("onEndTag");
090
091        private static final Collection.Key ATTRIBUTE_TYPE = KeyImpl.intern("attributetype");
092        private static final Collection.Key RT_EXPR_VALUE = KeyImpl.intern("rtexprvalue");
093        private static final String MARKER = "2w12801";
094        
095    /**
096     * Field <code>attributesScope</code>
097     */
098    // new scopes
099    protected StructImpl attributesScope;
100    private Caller callerScope;
101    private StructImpl thistagScope;
102
103    private Variables ctVariablesScope;
104
105    private boolean hasBody;
106
107    /**
108     * Field <code>filename</code>
109     */
110    //protected String filename;
111
112    /**
113     * Field <code>source</code>
114     */
115    protected InitFile source;
116    private String appendix;
117        
118        private Component cfc;
119        private boolean isEndTag;
120        
121        
122        
123    /**
124    * constructor for the tag class
125    **/
126    public CFTag() {
127        attributesScope = new StructImpl();
128        callerScope = new CallerImpl();
129        //thistagScope = new StructImpl();
130    }
131
132    @Override
133    public void setDynamicAttribute(String uri, String name, Object value) {
134        TagUtil.setDynamicAttribute(attributesScope,KeyImpl.init(name),value,TagUtil.ORIGINAL_CASE);
135    }
136
137    @Override
138    public void setDynamicAttribute(String uri, Collection.Key name, Object value) {
139        TagUtil.setDynamicAttribute(attributesScope,name,value,TagUtil.ORIGINAL_CASE);
140    }
141
142    @Override
143    public void release()   {
144        super.release();
145
146        hasBody=false;
147        //filename=null;      
148
149        attributesScope=new StructImpl();//.clear();
150        callerScope = new CallerImpl();
151        if(thistagScope!=null)thistagScope=null;
152        if(ctVariablesScope!=null)ctVariablesScope=null;  
153        
154
155        isEndTag=false;     
156        
157        //cfc=null;
158        source=null;
159    }
160
161    /**
162     * sets the appendix of the class
163     * @param appendix
164     */
165    public void setAppendix(String appendix) {
166        this.appendix=appendix;
167        //filename = appendix+'.'+pageContext.getConfig().getCFMLExtension();
168    }
169
170    @Override
171    public int doStartTag() throws PageException    {
172        PageContextImpl pci=(PageContextImpl) pageContext;
173                boolean old=pci.useSpecialMappings(true);
174                try{
175                        initFile();
176                callerScope.initialize(pageContext);
177                if(source.isCFC())return cfcStartTag();
178                return cfmlStartTag();  
179                }
180                finally{
181                        pci.useSpecialMappings(old);
182                }
183    }
184
185    @Override
186    public int doEndTag()   {
187        PageContextImpl pci=(PageContextImpl) pageContext;
188                boolean old=pci.useSpecialMappings(true);
189                try{
190                        if(source.isCFC())_doCFCFinally();
191                return EVAL_PAGE;
192                }
193                finally{
194                        pci.useSpecialMappings(old);
195                }
196    }
197
198    @Override
199    public void doInitBody()    {
200        
201    }
202
203    @Override
204    public int doAfterBody() throws PageException   {
205        if(source.isCFC())return cfcEndTag();
206        return cfmlEndTag();
207    }
208    
209
210        @Override
211    public void doCatch(Throwable t) throws Throwable {
212        if(source.isCFC()){
213                String source=isEndTag?"end":"body";
214                isEndTag=false;
215                _doCFCCatch(t,source);
216        }
217        else super.doCatch(t);
218        }
219    
220    void initFile() throws PageException {
221        source=initFile(pageContext);
222    }
223
224    public InitFile initFile(PageContext pageContext) throws PageException {
225        return CustomTagUtil.loadInitFile(pageContext, appendix);
226    }
227    
228        private int cfmlStartTag() throws PageException {
229                callerScope.initialize(pageContext);
230        
231                // thistag
232                if(thistagScope==null)thistagScope=new StructImpl(StructImpl.TYPE_LINKED);
233        thistagScope.set(GENERATED_CONTENT,"");
234        thistagScope.set(EXECUTION_MODE,"start");      
235        thistagScope.set(EXECUTE_BODY,Boolean.TRUE);
236        thistagScope.set(KeyConstants._HASENDTAG,Caster.toBoolean(hasBody));
237        
238                
239                ctVariablesScope=new VariablesImpl();
240        ctVariablesScope.setEL(KeyConstants._ATTRIBUTES,attributesScope);
241        ctVariablesScope.setEL(KeyConstants._CALLER,callerScope);
242        ctVariablesScope.setEL(KeyConstants._THISTAG,thistagScope);
243        
244        
245        // include
246        doInclude();
247        
248        return Caster.toBooleanValue(thistagScope.get(EXECUTE_BODY))?EVAL_BODY_BUFFERED:SKIP_BODY;
249    } 
250        
251    private int cfmlEndTag() throws PageException {
252        // thistag     
253        String genConBefore = bodyContent.getString();
254        thistagScope.set(GENERATED_CONTENT,genConBefore);
255        thistagScope.set(EXECUTION_MODE,"end");
256        thistagScope.set(EXECUTE_BODY,Boolean.FALSE);
257        writeEL(bodyContent, MARKER);
258        
259        // include
260        try{
261                doInclude();
262        }
263        catch(Throwable t){
264                        ExceptionUtil.rethrowIfNecessary(t);
265                writeOut(genConBefore);
266                throw Caster.toPageException(t);
267        }
268        
269        writeOut(genConBefore);
270
271        return Caster.toBooleanValue(thistagScope.get(EXECUTE_BODY))?EVAL_BODY_BUFFERED:SKIP_BODY;
272    }
273
274    
275
276        private void writeOut(String genConBefore) throws PageException {
277                String output = bodyContent.getString(); 
278                bodyContent.clearBody();
279        String genConAfter = Caster.toString(thistagScope.get(GENERATED_CONTENT));
280        
281        if(genConBefore!=genConAfter){
282                if(output.startsWith(genConBefore+MARKER)){
283                        output=output.substring((genConBefore+MARKER).length());
284                }
285                output=genConAfter+output;
286        }
287        else {
288                if(output.startsWith(genConBefore+MARKER)){
289                        output=output.substring((genConBefore+MARKER).length());
290                        output=genConBefore+output;
291                }
292        }
293        
294        
295        writeEL(bodyContent.getEnclosingWriter(),output);
296        }
297
298        private void writeEL(JspWriter writer, String str) throws PageException {
299                try {
300                        writer.write(str);
301                } catch (IOException e) {
302                        throw Caster.toPageException(e);
303                }
304        }
305
306        void doInclude() throws PageException {
307        Variables var=pageContext.variablesScope();
308        pageContext.setVariablesScope(ctVariablesScope);
309        
310        
311        QueryStack cs=null;
312        Undefined undefined=pageContext.undefinedScope();
313        int oldMode=undefined.setMode(Undefined.MODE_NO_LOCAL_AND_ARGUMENTS);
314        if(oldMode!=Undefined.MODE_NO_LOCAL_AND_ARGUMENTS)
315                callerScope.setScope(var,pageContext.localScope(),pageContext.argumentsScope(),true);
316        else 
317                callerScope.setScope(var,null,null,false);
318        
319        if(pageContext.getConfig().allowImplicidQueryCall()) {
320            cs=undefined.getQueryStack();
321            undefined.setQueryStack(new QueryStackImpl());
322        }
323            
324        try {
325            pageContext.doInclude(new PageSource[]{source.getPageSource()},false);
326        }
327        catch (Throwable t) {
328                ExceptionUtil.rethrowIfNecessary(t);
329            throw Caster.toPageException(t);
330        }
331        finally {
332            undefined.setMode(oldMode);
333            //varScopeData=variablesScope.getMap();
334            pageContext.setVariablesScope(var);
335            if(pageContext.getConfig().allowImplicidQueryCall()) {
336                undefined.setQueryStack(cs);
337            }
338        }
339    
340    }
341    
342    
343    // CFC
344    
345    private int cfcStartTag() throws PageException {
346        
347        callerScope.initialize(pageContext);
348        try {
349                        cfc = ComponentLoader.loadComponent(pageContext,null,source.getPageSource(), source.getFilename().substring(0,source.getFilename().length()-(pageContext.getConfig().getCFCExtension().length()+1)), false,true);
350                }
351        catch (TemplateException te) {
352                throw te;
353        }
354                catch (PageException e) {
355                        Mapping m = source.getPageSource().getMapping();
356                        //Physical:/Users/mic/Projects/Lucee/work/context/library/tag;
357                        
358                        ConfigWebImpl c=(ConfigWebImpl) pageContext.getConfig();
359                        if(m==c.getTagMapping()) m=c.getServerTagMapping();
360                        else m=null;
361                        // is te page source from a tag mapping, so perhaps it was moved from server to web context
362                        if(m!=null){
363                                PageSource ps = m.getPageSource(source.getFilename());
364                                try {
365                                        cfc = ComponentLoader.loadComponent(pageContext,null,ps, source.getFilename().substring(0,source.getFilename().length()-(pageContext.getConfig().getCFCExtension().length()+1)), false,true);
366                                }
367                                catch (PageException e1) {
368                                        throw e;
369                                }
370                                
371                        }
372                }
373                validateAttributes(cfc,attributesScope,StringUtil.ucFirst(ListUtil.last(source.getPageSource().getComponentName(),'.')));
374
375        boolean exeBody = false;
376        try     {
377                        Object rtn=Boolean.TRUE;
378                        if(cfc.contains(pageContext, KeyConstants._init)){
379                        Tag parent=getParent();
380                        while(parent!=null && !(parent instanceof CFTag && ((CFTag)parent).isCFCBasedCustomTag())) {
381                                parent=parent.getParent();
382                        }
383                                Struct args=new StructImpl(StructImpl.TYPE_LINKED);
384                                args.set(KeyConstants._HASENDTAG, Caster.toBoolean(hasBody));
385                        if(parent instanceof CFTag) {
386                                args.set(PARENT, ((CFTag)parent).getComponent());
387                        }
388                        rtn=cfc.callWithNamedValues(pageContext, KeyConstants._init, args);
389                }
390                        
391                if(cfc.contains(pageContext, ON_START_TAG)){
392                        Struct args=new StructImpl();
393                        args.set(KeyConstants._ATTRIBUTES, attributesScope);
394                        setCaller(pageContext,args);
395                        
396                        rtn=cfc.callWithNamedValues(pageContext, ON_START_TAG, args);   
397                    }
398                exeBody=Caster.toBooleanValue(rtn,true);
399        }
400        catch(Throwable t){
401                        ExceptionUtil.rethrowIfNecessary(t);
402                _doCFCCatch(t,"start");
403        }
404        return exeBody?EVAL_BODY_BUFFERED:SKIP_BODY;
405    }
406    
407    private void setCaller(PageContext pageContext, Struct args) throws PageException {
408        callerScope.initialize(pageContext);
409        boolean checkAgs=pageContext.undefinedScope().getCheckArguments();
410        if(checkAgs)
411                callerScope.setScope(pageContext.variablesScope(),pageContext.localScope(),pageContext.argumentsScope(),true);
412        else 
413                callerScope.setScope(pageContext.variablesScope(),null,null,false);
414        
415        
416        args.set(KeyConstants._CALLER, callerScope);
417        
418        
419        
420        //args.set(KeyConstants._CALLER, Duplicator.duplicate(pageContext.undefinedScope(),false));
421        }
422
423        private static void validateAttributes(Component cfc,StructImpl attributesScope,String tagName) throws ApplicationException, ExpressionException {
424                
425                TagLibTag tag=getAttributeRequirments(cfc,false);
426                if(tag==null) return;
427                
428                if(tag.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_FIXED || tag.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_MIXED){
429                        Iterator<Entry<String, TagLibTagAttr>> it = tag.getAttributes().entrySet().iterator();
430                        int count=0;
431                        Collection.Key key;
432                        TagLibTagAttr attr;
433                        Object value;
434                        Entry<String, TagLibTagAttr> entry;
435                        // check existing attributes
436                        while(it.hasNext()){
437                                entry = it.next();
438                                count++;
439                                key=KeyImpl.toKey(entry.getKey(),null);
440                                attr=entry.getValue();
441                                value=attributesScope.get(key,null);
442                                
443                                // check alias
444                                if(value==null) {
445                                        String[] alias = attr.getAlias();
446                                        if(!ArrayUtil.isEmpty(alias))for(int i=0;i<alias.length;i++){
447                                                value=attributesScope.get(KeyImpl.toKey(alias[i],null),null);
448                                                if(value!=null) break;
449                                        }
450                                }
451                                if(value==null){
452                                        if(attr.getDefaultValue()!=null){
453                                                value=attr.getDefaultValue();
454                                                attributesScope.setEL(key, value);
455                                        }
456                                        else if(attr.isRequired())
457                                                throw new ApplicationException("attribute ["+key.getString()+"] is required for tag ["+tagName+"]");
458                                }
459                                if(value!=null) {
460                                        if(!Decision.isCastableTo(attr.getType(),value,true,true,-1)) 
461                                                throw new CasterException(createMessage(attr.getType(), value));
462                                
463                                }
464                        }
465                        
466                        // check if there are attributes not supported
467                        if(tag.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_FIXED && count<attributesScope.size()){
468                                Collection.Key[] keys = attributesScope.keys();
469                                for(int i=0;i<keys.length;i++){
470                                        if(tag.getAttribute(keys[i].getLowerString(),true)==null)
471                                                throw new ApplicationException("attribute ["+keys[i].getString()+"] is not supported for tag ["+tagName+"]");
472                                }
473                                
474                                //Attribute susi is not allowed for tag cfmail
475                        }
476                }
477        }
478    
479    private static String createMessage(String type, Object value) {
480        if(value instanceof String) return "can't cast String ["+value+"] to a value of type ["+type+"]";
481        else if(value!=null) return "can't cast Object type ["+Type.getName(value)+"] to a value of type ["+type+"]";
482                else return "can't cast Null value to value of type ["+type+"]";
483
484    } 
485    
486
487        private static TagLibTag getAttributeRequirments(Component cfc, boolean runtime) {
488                Struct meta=null;
489        Member mem = ComponentProUtil.getMember(cfc,Component.ACCESS_PRIVATE, KeyConstants._metadata,true,false);
490        if(mem!=null)meta = Caster.toStruct(mem.getValue(),null,false);
491                if(meta==null) return null;
492        
493        TagLibTag tag=new TagLibTag(null);
494    // TAG
495
496        // type         
497        String type=Caster.toString(meta.get(ATTRIBUTE_TYPE,"dynamic"),"dynamic");
498        
499        if("fixed".equalsIgnoreCase(type))tag.setAttributeType(TagLibTag.ATTRIBUTE_TYPE_FIXED);
500        //else if("mixed".equalsIgnoreCase(type))tag.setAttributeType(TagLibTag.ATTRIBUTE_TYPE_MIXED);
501        //else if("noname".equalsIgnoreCase(type))tag.setAttributeType(TagLibTag.ATTRIBUTE_TYPE_NONAME);
502        else tag.setAttributeType(TagLibTag.ATTRIBUTE_TYPE_DYNAMIC);
503        
504        if(!runtime){
505                // hint
506                String hint=Caster.toString(meta.get(KeyConstants._hint,null),null);
507                if(!StringUtil.isEmpty(hint))tag.setDescription(hint);
508        }
509        
510    // ATTRIBUTES
511        Struct attributes=Caster.toStruct(meta.get(KeyConstants._ATTRIBUTES,null),null,false);
512        if(attributes!=null) {
513                Iterator<Entry<Key, Object>> it = attributes.entryIterator();
514                //Iterator it = attributes.entrySet().iterator();
515                Entry<Key, Object> entry;
516                TagLibTagAttr attr;
517                Struct sct;
518                String name;
519                Object defaultValue;
520                while(it.hasNext()){
521                        entry=it.next();
522                        name=Caster.toString(entry.getKey(),null);
523                        if(StringUtil.isEmpty(name)) continue;
524                        attr=new TagLibTagAttr(tag);
525                        attr.setName(name);
526                        
527                        sct=Caster.toStruct(entry.getValue(),null,false);
528                        if(sct!=null){
529                                attr.setRequired(Caster.toBooleanValue(sct.get(KeyConstants._required,Boolean.FALSE),false));
530                                attr.setType(Caster.toString(sct.get(KeyConstants._type,"any"),"any"));
531                                
532                                defaultValue= sct.get(KeyConstants._default,null);
533                                if(defaultValue!=null)attr.setDefaultValue(defaultValue);
534                                
535                                
536                                if(!runtime){
537                                        attr.setDescription(Caster.toString(sct.get(KeyConstants._hint,null),null));
538                                        attr.setRtexpr(Caster.toBooleanValue(sct.get(RT_EXPR_VALUE,Boolean.TRUE),true));
539                                }
540                        }
541                        tag.setAttribute(attr);
542                        
543                }
544        }
545        return tag;
546        }
547
548        private int cfcEndTag() throws PageException {
549        
550        boolean exeAgain = false;
551        try{
552                String output=null;
553                Object rtn=Boolean.FALSE;
554                
555                
556                if(cfc.contains(pageContext, ON_END_TAG)){
557                        try {
558                                output=bodyContent.getString();
559                            bodyContent.clearBody();
560                            //rtn=cfc.call(pageContext, ON_END_TAG, new Object[]{attributesScope,pageContext.variablesScope(),output});
561                            
562                            Struct args=new StructImpl(StructImpl.TYPE_LINKED);
563                                args.set(KeyConstants._ATTRIBUTES, attributesScope);
564                                setCaller(pageContext, args);
565                                args.set(GENERATED_CONTENT, output);
566                                rtn=cfc.callWithNamedValues(pageContext, ON_END_TAG, args);     
567                            
568                            
569                            
570                        }
571                        finally {
572                                writeEnclosingWriter();
573                        }
574                }
575                else writeEnclosingWriter();
576                
577                exeAgain= Caster.toBooleanValue(rtn,false);
578            }
579        catch(Throwable t){
580                        ExceptionUtil.rethrowIfNecessary(t);
581                isEndTag=true;
582                throw Caster.toPageException(t);
583        }
584        return exeAgain?EVAL_BODY_BUFFERED:SKIP_BODY;
585        
586    }
587    
588    public void _doCFCCatch(Throwable t, String source) throws PageException {
589        writeEnclosingWriter();
590        
591        // remove PageServletException wrap
592        if(t instanceof PageServletException) {
593                    PageServletException pse=(PageServletException)t;
594                    t=pse.getPageException();
595                }
596        
597        // abort 
598        try {
599                        if(lucee.runtime.exp.Abort.isAbort(t)){
600                                if(bodyContent!=null){
601                                        bodyContent.writeOut(bodyContent.getEnclosingWriter());
602                                        bodyContent.clearBuffer();
603                                }
604                                throw Caster.toPageException(t);
605                        }
606                }
607                catch(IOException ioe){
608                        throw Caster.toPageException(ioe);
609                }
610        
611        
612        
613        try {
614                        if(cfc.contains(pageContext, ON_ERROR)){
615                        PageException pe = Caster.toPageException(t);
616                        //Object rtn=cfc.call(pageContext, ON_ERROR, new Object[]{pe.getCatchBlock(pageContext),source});
617                        
618                        Struct args=new StructImpl(StructImpl.TYPE_LINKED);
619                        args.set(CFCATCH, pe.getCatchBlock(ThreadLocalPageContext.getConfig(pageContext)));
620                        args.set(SOURCE, source);
621                        Object rtn=cfc.callWithNamedValues(pageContext, ON_ERROR, args);        
622                    
623                        if(Caster.toBooleanValue(rtn,false))
624                                        throw t;
625                }
626                        else throw t;
627        }
628        catch(Throwable th) {
629                        ExceptionUtil.rethrowIfNecessary(th);
630                writeEnclosingWriter();
631                _doCFCFinally();
632                throw Caster.toPageException(th);
633        }
634        writeEnclosingWriter();
635        }
636    
637    private void _doCFCFinally() {
638                if(cfc.contains(pageContext, ON_FINALLY)){
639                        try {
640                                cfc.call(pageContext, ON_FINALLY, ArrayUtil.OBJECT_EMPTY);
641                        } 
642                        catch (PageException pe) {
643                                throw new PageRuntimeException(pe);
644                        }
645                        finally{
646                                writeEnclosingWriter();
647                        }
648        }
649        }
650    
651    
652    private void writeEnclosingWriter()  {
653        if(bodyContent!=null){
654                        try {
655                                String output = bodyContent.getString(); 
656                                bodyContent.clearBody();
657                    bodyContent.getEnclosingWriter().write(output);    
658                } 
659                        catch (IOException e) {
660                        //throw Caster.toPageException(e);
661                }
662        }
663        }
664    
665    
666
667    /**
668     * sets if tag has a body or not
669     * @param hasBody
670     */
671    public void hasBody(boolean hasBody) {
672        this.hasBody=hasBody;
673    }
674
675    /**
676     * @return Returns the appendix.
677     */
678    public String getAppendix() {
679        return appendix;
680    }
681
682    /**
683     * @return return thistag
684     */
685    public Struct getThis() {
686        if(isCFCBasedCustomTag()){
687                return cfc;
688        }
689        return thistagScope;
690    }
691    
692    /**
693     * @return return thistag
694     */
695    public Struct getCallerScope() {
696        return callerScope;
697    }
698    
699    /**
700     * @return return thistag
701     */
702    public Struct getAttributesScope() {
703        return attributesScope;
704    }
705
706        /**
707         * @return the ctVariablesScope
708         */
709        public Struct getVariablesScope() {
710                if(isCFCBasedCustomTag())       {
711                        return cfc.getComponentScope();
712                }
713                return ctVariablesScope;
714        }
715        
716    /**
717         * @return the cfc
718         */
719        public Component getComponent() {
720                return cfc;
721        }
722        
723        public boolean isCFCBasedCustomTag() {
724                return getSource().isCFC();
725        }
726        
727        private InitFile getSource() {
728                if(source==null){
729                        try {
730                                source=initFile(pageContext);
731                        } catch (PageException e) {
732                                e.printStackTrace();
733                        }
734                }
735                return source;
736        }
737
738        /*class InitFile {
739                PageSource ps;
740                String filename;
741                boolean isCFC;
742
743                public InitFile(PageSource ps,String filename,boolean isCFC){
744                        this.ps=ps;
745                        this.filename=filename;
746                        this.isCFC=isCFC;
747                }
748        }*/
749        
750        
751}