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}