PageRenderTime 106ms CodeModel.GetById 34ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 1ms

/src/aribaweb/ariba/ui/aribaweb/core/AWComponent.java

http://aribaweb.googlecode.com/
Java | 1886 lines | 1330 code | 235 blank | 321 comment | 193 complexity | 17d93633f0378329d0b4208c0ac2e2a6 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2    Copyright 1996-2011 Ariba, Inc.
   3
   4    Licensed under the Apache License, Version 2.0 (the "License");
   5    you may not use this file except in compliance with the License.
   6    You may obtain a copy of the License at
   7        http://www.apache.org/licenses/LICENSE-2.0
   8
   9    Unless required by applicable law or agreed to in writing, software
  10    distributed under the License is distributed on an "AS IS" BASIS,
  11    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    See the License for the specific language governing permissions and
  13    limitations under the License.
  14
  15    $Id: //ariba/platform/ui/aribaweb/ariba/ui/aribaweb/core/AWComponent.java#125 $
  16*/
  17
  18package ariba.ui.aribaweb.core;
  19
  20import ariba.ui.aribaweb.util.AWBaseObject;
  21import ariba.util.core.MapUtil;
  22import ariba.ui.aribaweb.util.AWCharacterEncoding;
  23import ariba.ui.aribaweb.util.AWEnvironmentStack;
  24import ariba.ui.aribaweb.util.AWFastStringBuffer;
  25import ariba.ui.aribaweb.util.AWGenericException;
  26import ariba.ui.aribaweb.util.AWEncodedString;
  27import ariba.ui.aribaweb.util.AWResource;
  28import ariba.ui.aribaweb.util.AWResourceManager;
  29import ariba.ui.aribaweb.util.AWStringDictionary;
  30import ariba.ui.aribaweb.util.AWStringsThunk;
  31import ariba.ui.aribaweb.util.AWUtil;
  32import ariba.ui.aribaweb.util.AWParameters;
  33import ariba.ui.aribaweb.util.AWNamespaceManager;
  34import ariba.ui.aribaweb.html.AWPrivateScript;
  35import ariba.util.core.Assert;
  36import ariba.util.core.ClassUtil;
  37import ariba.util.core.PerformanceState;
  38import ariba.util.core.StringUtil;
  39import ariba.util.core.Constants;
  40import ariba.util.core.Fmt;
  41import java.util.Map;
  42import ariba.util.i18n.I18NUtil;
  43import java.io.IOException;
  44import java.io.InputStream;
  45import java.io.UnsupportedEncodingException;
  46import java.lang.reflect.Field;
  47import java.lang.reflect.Method;
  48import java.util.Locale;
  49import java.util.TimeZone;
  50import java.util.Iterator;
  51import java.util.List;
  52import javax.servlet.http.HttpSession;
  53
  54/**
  55    AWComponent is the key class for template/based interactive content.  All "pages",
  56    page sub-sections, "wrappers", as well as most controls / widgets, and even control
  57    contructs are implemented as subclasses of AWComponent.
  58     <p/>
  59
  60    Components typically have a {@link AWTemplate template} (.awl file) containing "bare
  61    html text" mixed with {@link AWComponentReference references} to other embedded
  62    AWComponents (or low level {@link ariba.ui.aribaweb.core.AWBindableElement elements}).
  63    These, in turn, have {@link AWBinding bindings} that refer back to the java instance
  64    paired with the template (often using {@link ariba.util.fieldvalue.FieldPath fieldpaths}
  65    to dynamically push/pull values and invoke actions.
  66    <p/>
  67    Components may be either {@link #isStateless() stateless} (pooled) or stateful (bound to their
  68    page instance and stored in the session.  (Typically pages, page sub-sections, and
  69    particular rich components are stateful, while simple controls are stateless and
  70    simply push/pull their needed state from their parent component via bindings).
  71    <p/>
  72    Simple subclasses of AWComponent typically declare instance variables (possible public
  73    for use in bindings) as well as action methods (which return {@link AWResponseGenerating reponses}
  74    which are usually just other page-level AWComponent instances for the next page (or null
  75    to rerender the current page while reflecting any updated state).
  76    <p/>
  77    Components participate in the {@link AWCycleable} request handling lifecycle.  In addition to
  78    {@link #renderResponse(AWRequestContext, AWComponent)}, {@link #applyValues(AWRequestContext, AWComponent)},
  79    and {@link #invokeAction(AWRequestContext, AWComponent)}, an AWComponent also experiences
  80    {@link #init()}, {@link #awake()}, {@link #sleep()}, and even possibly {@link #hibernate()}.  
  81  @aribaapi private
  82 */
  83public class AWComponent extends AWBaseObject implements AWCycleable, AWCycleableReference, AWResponseGenerating,
  84        AWResponseGenerating.ResponseSubstitution
  85{                  
  86    public static final String ComponentTemplateFileExtension = ".awl";
  87    public static final Class ClassObject = AWComponent.class;
  88    private static AWTemplateParser DefaultTemplateParser = null;
  89    protected static final String BindingSuffix = "Binding";
  90    private static AWResourceManager _templateResourceManager = null;
  91
  92    protected AWComponentReference _componentReference;
  93    private AWPage _page;
  94    private AWComponent _parent;
  95    private AWElement _currentTemplateElement;
  96    private AWTemplate _uniqueTemplate;
  97    private Map _extendedFields;
  98    private AWBinding _otherBindingsBinding;
  99    private boolean _isAwake = false;
 100
 101    // to control wacky feature of allowing templates to specify their own encoding:
 102    private static boolean AllowTemplateEncoding = true;
 103
 104    // ** Thread Safety Considerations: Stateful components are never shared
 105    // between threads.  Stateless components are often used by different
 106    // threads, however, they are only used by one thread at a time as managed
 107    // by the check-out/in scheme in AWComponentDefinition, so we can consider
 108    // all components to be single threaded.
 109    public AWElement determineInstance (String elementName,
 110                                        String translatedClassName,
 111                                        Map bindingsHashtable,
 112                                        String templateName,
 113                                        int lineNumber)
 114    {
 115        AWApplication application = (AWApplication)AWConcreteApplication.SharedInstance;
 116        // Must search for elementName first since componentName will always succeed (eg AWComponent)
 117        // so we don't ever try the elementName (which is used for classless components).
 118        AWComponentDefinition componentDefinition = application.componentDefinitionForName(translatedClassName);
 119        if (componentDefinition == null) {
 120            String componentName = ClassUtil.stripPackageFromClassName(getClass().getName());
 121            componentDefinition = application.componentDefinitionForName(componentName);
 122        }
 123        AWComponentReference componentReference = createComponentReference(componentDefinition);
 124        return componentReference.determineInstance(translatedClassName, bindingsHashtable, templateName, lineNumber);
 125    }
 126
 127    protected AWComponentReference createComponentReference (AWComponentDefinition componentDefinition)
 128    {
 129        return AWComponentReference.create(componentDefinition);
 130    }
 131
 132    public AWElement determineInstance (String elementName, Map bindingsHashtable, String templateName, int lineNumber)
 133    {
 134        Assert.assertNonFatal(true, "Do not call determineInstance(String, Map, String, int)");
 135        return determineInstance(elementName, elementName, bindingsHashtable, null, -1);
 136    }
 137
 138    protected boolean useLocalPool ()
 139    {
 140        return false;
 141    }
 142
 143    /**
 144     * Pseudo-private: used only for initing validation testing version of components
 145     * (i.e. for AWConcreteApplication.preinstantiateAllComponents()
 146     */
 147    protected void _setup (AWComponentReference componentReference, AWPage page)
 148    {
 149        _componentReference = componentReference;
 150        setPage(page);
 151    }
 152
 153    public void init (AWComponentReference componentReference, AWComponent parentComponent, AWPage page)
 154    {
 155        _componentReference = componentReference;
 156        _parent = parentComponent;
 157        setPage(page);
 158        this.init();
 159    }
 160
 161    public void init ()
 162    {
 163        super.init();
 164
 165        // If we are truly stateless (isStateless == true and we aren't a page level
 166        // component) make sure the component doesn't override init.  It doesn't make
 167        // sense for stateless components to override init because init will only be
 168        // called once (technically, once per shared instance).
 169        if (isStatelessSubComponent()) {
 170            try {
 171                Class cls = getClass();
 172                while (cls != null && cls != AWComponent.ClassObject) {
 173                    Method initMethod = cls.getDeclaredMethod("init");
 174                    Assert.that(initMethod == null, "Stateless component %s cannot override init().", getClass().getName());
 175                    cls = cls.getSuperclass();
 176                }
 177            }
 178            catch (NoSuchMethodException e) {
 179                // Will never happen
 180                e.printStackTrace();
 181            }
 182        }
 183    }
 184
 185    protected void setParent (AWComponent parent)
 186    {
 187        _parent = parent;
 188    }
 189
 190    public AWComponent parent ()
 191    {
 192        return _parent;
 193    }
 194
 195    protected void setPage (AWPage page)
 196    {
 197        _page = page;
 198    }
 199
 200    private boolean isStatelessSubComponent ()
 201    {
 202        return isStateless() && parent() != null;
 203    }
 204
 205    protected void setCurrentTemplateElement (AWElement element)
 206    {
 207        _currentTemplateElement = element;
 208    }
 209
 210    protected AWElement currentTemplateElement ()
 211    {
 212        return _currentTemplateElement;
 213    }
 214
 215    /**
 216        This method allows for access to the current component fromthe .awl via "$this"
 217    */
 218    public AWComponent getThis ()
 219    {
 220        return this;
 221    }
 222
 223    public void setupForNextCycle (AWComponentReference componentReference, AWComponent parentComponent, AWPage page)
 224    {
 225        // This allows a component to be stateless -- these three things change each time a component is reused.
 226        _parent = parentComponent;
 227        _componentReference = componentReference;
 228        setPage(page);
 229    }
 230
 231    public AWComponentDefinition componentDefinition ()
 232    {
 233        return _componentReference.componentDefinition();
 234    }
 235
 236    public boolean hasMultipleTemplates ()
 237    {
 238        return false;
 239    }
 240
 241    public static void setDefaultTemplateParser (AWTemplateParser templateParser)
 242    {
 243        DefaultTemplateParser = templateParser;
 244    }
 245
 246    public static AWTemplateParser defaultTemplateParser ()
 247    {
 248        if (DefaultTemplateParser == null) {
 249            DefaultTemplateParser = new AWHtmlTemplateParser();
 250            DefaultTemplateParser.init((AWApplication)AWConcreteApplication.sharedInstance());
 251
 252            // the AWApi doc tags...
 253            DefaultTemplateParser.registerContainerClassForTagName("Binding", AWBindingApi.class);
 254            DefaultTemplateParser.registerContainerClassForTagName("NamedContent", AWContentApi.class);
 255            DefaultTemplateParser.registerContainerClassForTagName("Copyright", AWHtmlTemplateParser.LiteralContainer.class);
 256            DefaultTemplateParser.registerContainerClassForTagName("Responsible", AWHtmlTemplateParser.LiteralContainer.class);
 257            DefaultTemplateParser.registerContainerClassForTagName("Todo", AWHtmlTemplateParser.LiteralContainer.class);
 258            DefaultTemplateParser.registerContainerClassForTagName("Overview", AWHtmlTemplateParser.LiteralContainer.class);
 259            DefaultTemplateParser.registerContainerClassForTagName("Example", AWExampleApi.class);
 260            DefaultTemplateParser.registerContainerClassForTagName("IncludeExample", AWIncludeExample.class);
 261
 262            DefaultTemplateParser.registerContainerClassForTagName("script", AWPrivateScript.class);
 263            DefaultTemplateParser.registerContainerClassForTagName("Script", AWPrivateScript.class);
 264            DefaultTemplateParser.registerContainerClassForTagName("SCRIPT", AWPrivateScript.class);
 265        }
 266        return DefaultTemplateParser;
 267    }
 268
 269    protected static void initializeAllowedGlobalTags (AWNamespaceManager.AllowedGlobalsResolver resolver)
 270    {
 271        resolver.addAllowedGlobal("Binding");
 272        resolver.addAllowedGlobal("NamedContent");
 273        resolver.addAllowedGlobal("Copyright");
 274        resolver.addAllowedGlobal("Responsible");
 275        resolver.addAllowedGlobal("Todo");
 276        resolver.addAllowedGlobal("Overview");
 277        resolver.addAllowedGlobal("Example");
 278        resolver.addAllowedGlobal("IncludeExample");
 279        resolver.addAllowedGlobal("Script");
 280    }
 281    
 282    public AWTemplateParser templateParser ()
 283    {
 284        return _page.templateParser();
 285    }
 286
 287    public void setTemplateParser (AWTemplateParser templateParser)
 288    {
 289        _page.setTemplateParser(templateParser);
 290    }
 291
 292    public AWComponentReference componentReference ()
 293    {
 294        return _componentReference;
 295    }
 296
 297    public static AWComponent createPageWithName (String pageName)
 298    {
 299        AWConcreteApplication application = (AWConcreteApplication)AWConcreteApplication.sharedInstance();
 300        AWRequestContext requestContext = application.createRequestContext(null);
 301        return application.createPageWithName(pageName, requestContext);
 302    }
 303
 304    protected void takeBindings (AWBindingDictionary bindings)
 305    {
 306        if (isStateless()) {
 307            takeBindingsViaReflection(bindings);
 308        }
 309        _otherBindingsBinding = bindings.get(AWBindingNames.otherBindings);
 310    }
 311
 312    private void takeBindingsViaReflection (AWBindingDictionary bindings)
 313    {
 314        Map fields = componentDefinition().bindingFields();
 315        try {
 316            int i = bindings.size();
 317            while (i-- > 0) {
 318                String key = bindings.keyAt(i);
 319                Field field = (Field)fields.get(key);
 320                if (field != null) {
 321                    AWBinding binding = bindings.elementAt(i);
 322                    if (binding != null) {
 323                        // Run this check only in debug mode
 324                        if (((AWConcreteApplication)AWConcreteApplication.SharedInstance).isStateValidationEnabled()) {
 325                            Object existingValue = field.get(this);
 326                            Assert.that(existingValue == null || existingValue == AWBinding.DummyBinding,
 327                                        "AWBinding fields must not be initialized: %s %s contains %s",
 328                                        getClass(), field, existingValue);
 329                        }
 330                        field.set(this, binding);
 331                    }
 332                }
 333            }
 334        }
 335        catch (IllegalAccessException illegalAccessException) {
 336            throw new AWGenericException(illegalAccessException);
 337        }
 338    }
 339
 340    private void clearBindingIvars ()
 341    {
 342        Map fields = componentDefinition().bindingFields();
 343        try {
 344            Iterator iter = fields.values().iterator();
 345            while (iter.hasNext()) {
 346                Field field = (Field)iter.next();
 347                field.set(this, null);
 348            }
 349        }
 350        catch (IllegalAccessException illegalAccessException) {
 351            throw new AWGenericException(illegalAccessException);
 352        }
 353    }
 354
 355    // Called when checking a stateless component back into its pool
 356    protected boolean isFieldRequiredClear (Field field) {
 357        // we don't require AWBindings to be cleared for locally pooled components (because they're pooled on the
 358        // AWComponentReference where the bindings stay constant across uses
 359        return super.isFieldRequiredClear(field) &&
 360                 (!useLocalPool() || !AWBinding.class.isAssignableFrom(field.getType()))
 361                && !(field.getName().equals("_uniqueTemplate") || field.getName().equals("_extendedFields"));
 362    }
 363
 364    public void ensureFieldValuesClear ()
 365    {
 366        // Stop the check at AWComponent -- i.e. just check ivars introduced in the
 367        // subclasses
 368        ensureFieldValuesClear(getClass(), AWComponent.class);
 369    }
 370
 371    protected Field lookupBindingField (String fieldName, Class classObject)
 372    {
 373        Field field = null;
 374        try {
 375            field = classObject.getDeclaredField(fieldName);
 376            if (field == null) {
 377                field = recurseForLookupBindingField(fieldName, classObject);
 378            }
 379        }
 380        catch (NoSuchFieldException noSuchFieldException) {
 381            field = recurseForLookupBindingField(fieldName, classObject);
 382        }
 383        catch (SecurityException securityException) {
 384            securityException = null;
 385        }
 386        return field;
 387    }
 388
 389    private Field recurseForLookupBindingField (String fieldName, Class classObject)
 390    {
 391        Class superclassObject = classObject.getSuperclass();
 392        return (superclassObject == null || superclassObject == AWComponent.ClassObject.getSuperclass()) ?
 393            null :
 394            lookupBindingField(fieldName, superclassObject);
 395    }
 396
 397    protected Field lookupBindingField (String key)
 398    {
 399        String fieldName = StringUtil.strcat("_", key, BindingSuffix);
 400        return lookupBindingField(fieldName, getClass());
 401    }
 402
 403    /////////////////////////////
 404    // Request Context Accessors
 405    /////////////////////////////
 406    public AWRequestContext requestContext ()
 407    {
 408        return _page.requestContext();
 409    }
 410
 411    public AWApplication application ()
 412    {
 413        return _page.requestContext().application();
 414    }
 415
 416    public AWPage page ()
 417    {
 418        return _page;
 419    }
 420
 421    public AWComponent pageComponent ()
 422    {
 423        return _page.pageComponent();
 424    }
 425
 426    public AWSession session ()
 427    {
 428        return _page.session(true);
 429    }
 430
 431    public AWSession session (boolean required)
 432    {
 433        return _page.session(required);
 434    }
 435
 436    public HttpSession httpSession ()
 437    {
 438        return _page.httpSession();
 439    }
 440
 441    public AWRequest request ()
 442    {
 443        return _page.requestContext().request();
 444    }
 445
 446    public AWResponse response ()
 447    {
 448        return _page.requestContext().response();
 449    }
 450
 451    public boolean isBrowserMicrosoft ()
 452    {
 453        return _page.isBrowserMicrosoft;
 454    }
 455
 456    public boolean isMacintosh ()
 457    {
 458        return _page.isMacintosh;
 459    }
 460
 461    public String browserMinWidth ()
 462    {
 463        return _page.requestContext().browserMinWidth();
 464    }
 465
 466    public String browserMaxWidth ()
 467    {
 468        return _page.requestContext().browserMaxWidth();
 469    }
 470
 471    public AWResourceManager resourceManager ()
 472    {
 473        return _page.resourceManager();
 474    }
 475
 476    public void setResourceManager (AWResourceManager resourceManager)
 477    {
 478        _page.setResourceManager(resourceManager);
 479    }
 480
 481    public void setCharacterEncoding (AWCharacterEncoding characterEncoding)
 482    {
 483        _page.setCharacterEncoding(characterEncoding);
 484    }
 485
 486    public AWCharacterEncoding characterEncoding ()
 487    {
 488        return _page.characterEncoding();
 489    }
 490
 491    public void setClientTimeZone (TimeZone timeZone)
 492    {
 493        _page.setClientTimeZone(timeZone);
 494    }
 495
 496    public TimeZone clientTimeZone ()
 497    {
 498        return _page.clientTimeZone();
 499    }
 500
 501    public void setEnv (AWEnvironmentStack environmentStack)
 502    {
 503        _page.setEnv(environmentStack);
 504    }
 505
 506    public AWEnvironmentStack env ()
 507    {
 508        return _page.env();
 509    }
 510
 511    public void setPreferredLocale (Locale locale)
 512    {
 513        _page.setPreferredLocale(locale);
 514    }
 515
 516    public Locale preferredLocale ()
 517    {
 518        return _page.preferredLocale();
 519    }
 520
 521    public String name ()
 522    {
 523        return componentDefinition().componentName();
 524    }
 525
 526    public String namePath ()
 527    {
 528        return componentDefinition().componentNamePath();
 529    }
 530
 531    public String templateName ()
 532    {
 533        return componentDefinition().templateName();
 534    }
 535
 536    /**
 537     * Get the name of the class where we get our resources from.
 538     * This is the class in which we resolve AWLocal references.
 539     * @return the full class name of the resource class
 540     * @aribaapi ariba
 541     */
 542    public String resourceClassName ()
 543    {
 544        if (!hasMultipleTemplates()) {
 545            // in the normal case, we use the one-time calculated resourceClassName
 546            return componentDefinition().resourceClassName(this);
 547        }
 548        else {
 549            // in the multiple personalities case, get the template name and derive
 550            // a resource class from that.
 551            String templateName = templateName();
 552            return AWComponentDefinition.computeClassNameFromTemplate(templateName);
 553        }
 554    }
 555
 556    public AWTemplate template ()
 557    {
 558        AWTemplate template = _uniqueTemplate;
 559        if (template == null) {
 560            template = loadTemplate();
 561            if (!hasMultipleTemplates() && !AWConcreteApplication.IsRapidTurnaroundEnabled) {
 562                _uniqueTemplate = template;
 563            }
 564        }
 565        return template;
 566    }
 567
 568    public boolean isClientPanel ()
 569    {
 570        return dict("clientPanel") != null;
 571    }
 572
 573    public void setClientPanel (boolean yn)
 574    {
 575        if (yn) {
 576            dict("clientPanel", Boolean.TRUE);
 577        } else {
 578            dict().remove("clientPanel");
 579        }
 580    }
 581
 582    // AWResponseGenerating.ResponseSubstitution
 583    // See if we're a modal panel...
 584    public AWResponseGenerating replacementResponse () {
 585        AWPage requestPage;
 586        if (requestContext() != null  && (requestPage = requestContext().requestPage()) != null
 587                && isClientPanel()) {
 588            requestPage.addModalPanel(this);
 589            return requestPage.pageComponent();
 590        }
 591        return this;
 592    }
 593
 594    protected AWApi componentApi ()
 595    {
 596        // make sure the template is loaded
 597        if (hasTemplate()) {
 598            template();
 599            return componentDefinition().componentApi();
 600        }
 601        return null;
 602    }
 603
 604    ////////////////////
 605    // Validation
 606    ////////////////////
 607
 608    protected void addValidationError (AWRequestContext requestContext)
 609    {
 610//        AWValidationContext validationContext = requestContext.validationContext();
 611//        validationContext.addComponentWithError(componentDefinition());
 612    }
 613
 614    protected void validate (AWValidationContext validationContext)
 615    {
 616        AWTemplate template = template();
 617        if (template != null && !template.validated()) {
 618            template.validate(validationContext,this);
 619        }
 620        if (componentDefinition().hasValidationErrors() && isValidationEnabled()) {
 621            validationContext.addComponentWithError(componentDefinition());
 622        }
 623    }
 624
 625    protected String getCurrentPackageName ()
 626    {
 627        return componentDefinition().componentPackageName();
 628    }
 629
 630    // Override this method to bypass the package level check
 631    public boolean isValidationEnabled ()
 632    {
 633        return isPackageLevelFlagEnabled(getCurrentPackageName(), AWConcreteApplication.TemplateValidationFlag);
 634    }
 635
 636    // Override this method to bypass the package level check
 637    public boolean isStrictTagNaming ()
 638    {
 639        return isPackageLevelFlagEnabled(getCurrentPackageName(), AWConcreteApplication.StrictTagNamingFlag);
 640    }
 641
 642    protected boolean isPackageLevelFlagEnabled (String packageName, int flag)
 643    {
 644        return ((AWConcreteApplication)AWConcreteApplication.sharedInstance()).isPackageLevelFlagEnabled(packageName, flag);
 645    }
 646
 647    ////////////////////
 648    // Template parsing
 649    ////////////////////
 650    public boolean allowsWhitespaceCompression ()
 651    {
 652        return !AWConcreteApplication.IsDebuggingEnabled;
 653    }
 654
 655
 656    private void throwComponentNotFoundException (String templateName)
 657    {
 658        throw new AWGenericException("*** Error: " + getClass().getName() + ": unable to locate file named \"" + templateName + "\"");
 659    }
 660
 661    protected AWResource safeTemplateResource ()
 662    {
 663        AWResourceManager resourceManager = templateResourceManager();
 664        String templateName = templateName();
 665        AWResource resource = resourceManager.resourceNamed(templateName);
 666        if (resource == null) {
 667            resource = parentTemplateResource();
 668        }
 669        return resource;
 670    }
 671
 672    protected AWResource parentTemplateResource ()
 673    {
 674        // Recurse up the superclass chain to locate a template.  We used to require subclasses
 675        // to explicitly provide the templateName of the superclass, but now we iteratively
 676        // locate the template resource as follows:
 677        AWResource resource = null;
 678        AWResourceManager resourceManager = templateResourceManager();
 679        Class superclass = getClass().getSuperclass();
 680        while (resource == null && AWComponent.ClassObject.isAssignableFrom(superclass)) {
 681            String superclassName = superclass.getName();
 682            AWComponentDefinition componentDefinition = application().componentDefinitionForName(superclassName);
 683            String templateName = componentDefinition.templateName();
 684            resource = resourceManager.resourceNamed(templateName);
 685            superclass = superclass.getSuperclass();
 686        }
 687        return resource;
 688    }
 689
 690    public AWResource templateResource ()
 691    {
 692        AWResource resource = safeTemplateResource();
 693        if (resource == null) {
 694            throwComponentNotFoundException(templateName());
 695        }
 696        return resource;
 697    }
 698
 699
 700    public static AWResourceManager templateResourceManager ()
 701    {
 702        return _templateResourceManager;
 703    }
 704
 705    /**
 706     * We need to use the same resource manager for loading templates.
 707     * Otherwise, each component instances can potential get templates with
 708     * a different structure (ie, AWIncludeComponent maps)
 709     */
 710    public static void initTemplateResourceManager (AWResourceManager manager)
 711    {
 712        _templateResourceManager = manager;
 713    }
 714
 715    protected boolean hasTemplate ()
 716    {
 717        return safeTemplateResource() != null;
 718    }
 719
 720    protected boolean templateHasEncoding ()
 721    {
 722        return false;
 723    }
 724
 725    public boolean hasContentNamed (String name)
 726    {
 727        AWElement contentElement = componentReference().contentElement();
 728        if (name == null) return (contentElement != null);
 729        
 730        if (contentElement instanceof AWTemplate) {
 731            AWTemplate template = (AWTemplate)contentElement;
 732            int index = template.indexOfNamedSubtemplate(name, this);
 733            if (index != -1) {
 734                AWContent content = template.subtemplateAt(index);
 735                return content.enabled(this);
 736            }
 737        }
 738        return false;
 739    }
 740
 741    /**
 742     * @deprecated  Use hasContentNamed()
 743     */
 744    public boolean hasSubTemplateNamed (String templateName)
 745    {
 746        return hasContentNamed(templateName);
 747    }
 748
 749    protected boolean doesReferenceHaveNamedTemplate (String templateName)
 750    {
 751        AWComponentReference componentReference = componentReference();
 752        return componentReference == null ? false : componentReference.hasNamedTemplate(parent(), templateName);
 753    }
 754
 755    protected static String readTemplateString (InputStream inputStream)
 756    {
 757        byte[] bytes = AWUtil.getBytes(inputStream);
 758        String templateString = AWCharacterEncoding.ISO8859_1.newString(bytes);
 759        if (AllowTemplateEncoding && templateString.startsWith("<!--")) {
 760            int indexOfClosingComment = templateString.indexOf("-->");
 761            if (indexOfClosingComment != -1) {
 762                String firstLine = templateString.substring(0, indexOfClosingComment);
 763                String encodingToken = "encoding:";
 764                int indexOfEncoding = AWUtil.indexOf(firstLine, encodingToken, 0, true);
 765                if (indexOfEncoding != -1) {
 766                    String encodingName = firstLine.substring(indexOfEncoding + encodingToken.length(), indexOfClosingComment);
 767                    encodingName = encodingName.trim();
 768                    try {
 769                        templateString = new String(bytes, encodingName);
 770                    }
 771                    catch (UnsupportedEncodingException unsupportedEncodingException) {
 772                        throw new AWGenericException(unsupportedEncodingException);
 773                    }
 774                }
 775            }
 776        }
 777        return templateString;
 778    }
 779
 780    protected InputStream templateInputStream ()
 781    {
 782        AWResource templateResource = templateResource();
 783        return templateResource.inputStream();
 784    }
 785
 786    protected AWTemplate parseTemplate (InputStream fileInputStream)
 787    {
 788        AWTemplate template = null;
 789        AWTemplateParser templateParser = templateParser();
 790        try {
 791            String templateString = null;
 792            boolean shouldExpectEncoding = templateHasEncoding();
 793            if (shouldExpectEncoding) {
 794                templateString = AWUtil.stringWithContentsOfInputStream(fileInputStream, shouldExpectEncoding);
 795            }
 796            else {
 797                templateString = readTemplateString(fileInputStream);
 798            }
 799            if (allowsWhitespaceCompression()) {
 800                templateString = AWUtil.leftJustify(templateString);
 801            }
 802            if (templateParser instanceof AWHtmlTemplateParser) {
 803                template = ((AWHtmlTemplateParser)templateParser).templateFromString(templateString, templateName(), this);
 804            }
 805            else {
 806                template = templateParser.templateFromString(templateString, templateName());
 807            }
 808        }
 809        finally {
 810            try {
 811                fileInputStream.close();
 812            }
 813            catch (IOException ioexception) {
 814                String message = Fmt.S("Error closing file: \"%s\"", templateName());
 815                throw new AWGenericException(message, ioexception);
 816            }
 817        }
 818        return template;
 819    }
 820
 821    protected AWTemplate parseTemplate ()
 822    {
 823        InputStream fileInputStream = templateInputStream();
 824        return parseTemplate(fileInputStream);
 825    }
 826
 827    public AWTemplate loadTemplate ()
 828    {
 829        AWTemplate template = null;
 830        AWResource resource = templateResource();
 831        if (resource != null) {
 832            template = (AWTemplate)resource.object();
 833            if ((template == null) ||
 834                (AWConcreteApplication.IsRapidTurnaroundEnabled
 835                        && requestContext().currentPhase() == AWRequestContext.Phase_Render
 836                        && resource.hasChanged())) {
 837
 838                // reset needs to be called before template parser since the template parser
 839                // can append validation errors
 840                componentDefinition().resetValidationData();
 841
 842                template = parseTemplate();
 843
 844                if (template != null) {
 845                    AWApi componentApi  = ((AWConcreteTemplate)template).removeApiTag();
 846
 847                    if (AWConcreteApplication.IsDebuggingEnabled) {
 848                        componentDefinition().setComponentApi(componentApi);
 849
 850                        // validate the componentAPI.  Needed since componentAPI
 851                        // validation initializes supported binding information.
 852
 853                        if (componentApi != null && requestContext() != null) {
 854                            // if there is a component api, then validate it (metadata validation)
 855                            componentApi.validate(requestContext().validationContext(), this);
 856                        }
 857                    }
 858
 859                    resource.setObject(template);
 860                }
 861            }
 862        }
 863        return template;
 864    }
 865
 866    ////////////////////
 867    // Parser callbacks
 868    ////////////////////
 869    public boolean allowEmbeddedKeyPaths ()
 870    {
 871        return true;
 872    }
 873
 874    /**
 875       Wacky: this method is called when this (stateful) component instance that is about
 876       to be activate is using an out of date class (do to dynamic reloading).  If a non-null
 877       instance is returned, it is used in place of this instance.  If null is returned,
 878       the stale instance is used.
 879
 880       This is only called in dubug mode (i.e. IsRapidTurnaroundEnabled == true)
 881    */
 882    protected AWComponent replacementInstance (AWComponent parentComponent)
 883    {
 884        AWComponent replacement = null;
 885
 886        // by default, stateful *sub*-components are replaced, but pageComponents are not
 887        if (parentComponent != null) {
 888            replacement = componentDefinition().createComponent(componentReference(), parentComponent, requestContext());
 889            replacement.ensureAwake(page());
 890        }
 891        return replacement;
 892    }
 893
 894
 895    /**
 896        Overridden by AWComponent subclasses to indicate whether component instances
 897        should be preseved with the page/session (i.e. are "stateful") or can be
 898        pooled and reused for each phase of request processing (i.e. are stateless)
 899
 900        Default is to be statelss unless the component is used as the top-level
 901        (page) component.
 902     */
 903    public boolean isStateless ()
 904    {
 905        // default is true (and by default components aren't client panels...)
 906        return !isClientPanel();
 907    }
 908
 909    protected void saveInPage (AWElementIdPath elementIdPath)
 910    {
 911        requestContext().putStatefulComponent(elementIdPath, this);
 912    }
 913
 914    ///////////////
 915    // AWCycleable
 916    ///////////////
 917    public void applyValues(AWRequestContext requestContext, AWComponent component)
 918    {
 919        template().applyValues(requestContext, this);
 920    }
 921
 922    public AWResponseGenerating invokeAction(AWRequestContext requestContext, AWComponent component)
 923    {
 924        return template().invokeAction(requestContext, this);
 925    }
 926
 927    public void renderResponse(AWRequestContext requestContext, AWComponent component)
 928    {
 929        if (AWConcreteApplication.IsDebuggingEnabled) {
 930            validate(requestContext.validationContext());
 931        }
 932
 933        if (!_componentReference.isStateless()) {
 934            AWBacktrackState backtrackState = requestContext.backtrackState();
 935            if ((backtrackState != null) && (backtrackState.component == this)) {
 936                Object userBacktrackState = backtrackState.userState;
 937                Object existingUserBacktrackState = restoreFromBacktrackState(userBacktrackState);
 938                _page.swapBacktrackStates(existingUserBacktrackState);
 939            }
 940        }
 941        template().renderResponse(requestContext, this);
 942    }
 943
 944    // these should be called when not calling via ComponentReference
 945    public void _topLevelApplyValues (AWRequestContext requestContext, AWComponent component)
 946    {
 947        applyValues(requestContext, component);
 948    }
 949
 950    public AWResponseGenerating _topLevelInvokeAction(AWRequestContext requestContext, AWComponent component)
 951    {
 952        return invokeAction(requestContext, this);
 953    }
 954
 955    public void _topLevelRenderResponse(AWRequestContext requestContext, AWComponent component)
 956    {
 957        renderResponse(requestContext, this);
 958    }
 959
 960
 961    public AWEncodedString escapeAttribute (AWEncodedString attributeValue)
 962    {
 963        return useXmlEscaping() ? attributeValue.xmlEscapedString() : attributeValue.htmlAttributeString();
 964    }
 965
 966    public AWEncodedString escapeString (Object object)
 967    {
 968        return useXmlEscaping() ? AWUtil.escapeXml(object) : AWUtil.escapeHtml(object);
 969    }
 970
 971    public AWEncodedString escapeUnsafeString (Object object)
 972    {
 973        return useXmlEscaping() ? AWUtil.escapeXml(object) :
 974                                  AWUtil.escapeUnsafeHtml(object);
 975    }
 976
 977    public boolean shouldCloseElements ()
 978    {
 979        return useXmlEscaping();
 980    }
 981
 982    public boolean useXmlEscaping ()
 983    {
 984        return false;
 985    }
 986
 987    //////////////////////////
 988    // Page Cache Management
 989    //////////////////////////
 990    public void recordBacktrackState (Object userBacktrackState)
 991    {
 992        if (_componentReference.isStateless()) {
 993            throw new AWGenericException("Attempt to recordBacktrackState(Object) from stateless component.");
 994        }
 995        _page.recordBacktrackState(this, userBacktrackState);
 996    }
 997
 998    public void recordBacktrackState (int userBacktrackState)
 999    {
1000        recordBacktrackState(Constants.getInteger(userBacktrackState));
1001    }
1002
1003    public void removeBacktrackState ()
1004    {
1005        _page.removeBacktrackState();
1006    }
1007
1008    public void truncateBacktrackState ()
1009    {
1010        _page.truncateBacktrackState();
1011    }
1012
1013    public void truncateBacktrackState (AWBacktrackState backtrackStateMark)
1014    {
1015        _page.truncateBacktrackState(backtrackStateMark);
1016    }
1017
1018    public AWBacktrackState markBacktrackState ()
1019    {
1020        return _page.markBacktrackState();
1021    }
1022
1023    public Object restoreFromBacktrackState (Object userBacktrackState)
1024    {
1025        throw new AWGenericException(getClass().getName() + ": backtrack state was found, but component did not implement takeValuesFromBacktrackState(Object userState)");
1026    }
1027
1028    protected boolean shouldRedirect ()
1029    {
1030        return true;
1031    }
1032
1033    public boolean shouldCachePage ()
1034    {
1035        return true;
1036    }
1037
1038    ////////////////////////
1039    // AWResponseGenerating
1040    ////////////////////////
1041    public AWResponse generateResponse (AWResponse response, AWRequestContext requestContext)
1042    {
1043        _page.ensureAwake(requestContext);
1044        requestContext.setPage(_page);
1045        return requestContext.generateResponse(response);
1046    }
1047
1048    public AWResponse generateResponse (AWResponse response)
1049    {
1050        AWRequestContext existingRequestContext = requestContext();
1051        AWRequest request = existingRequestContext == null ? null : existingRequestContext.request();
1052        AWApplication application = (AWApplication)AWConcreteApplication.sharedInstance();
1053        AWRequestContext requestContext = application.createRequestContext(request);
1054        if (existingRequestContext != null) {
1055            HttpSession existingHttpSession = existingRequestContext.existingHttpSession();
1056            if (existingHttpSession != null) {
1057                requestContext.setHttpSession(existingHttpSession);
1058            }
1059        }
1060        return generateResponse(response, requestContext);
1061    }
1062
1063    public AWResponse generateResponse ()
1064    {
1065        return generateResponse(null);
1066    }
1067
1068    public String generateStringContents ()
1069    {
1070        AWRequestContext newRequestContext = null;
1071        AWApplication application = (AWApplication)AWConcreteApplication.sharedInstance();
1072        AWRequestContext existingRequestContext = requestContext();
1073        if (existingRequestContext != null) {
1074            newRequestContext = application.createRequestContext(existingRequestContext.request());
1075            HttpSession existingHttpSession =
1076                existingRequestContext.existingHttpSession();
1077            newRequestContext.setHttpSession(existingHttpSession);
1078        }
1079        else {
1080            newRequestContext = application.createRequestContext(null);
1081        }
1082
1083        // disable incremental update since we're trying to get the string contents
1084        newRequestContext.isContentGeneration(true);
1085
1086        AWResponse response = generateResponse(null, newRequestContext);
1087        return response.generateStringContents();
1088    }
1089
1090    /////////////
1091    // Awake
1092    /////////////
1093    protected void awake ()
1094    {
1095        // Default is to do nothing.  Users can override, but are not required to call super.
1096    }
1097
1098    protected void flushState ()
1099    {
1100        clearDict(this);
1101    }
1102
1103    public void ensureAwake (AWPage page)
1104    {
1105        if (!_isAwake) {
1106            setPage(page);
1107            awake();
1108            _isAwake = true;
1109        }
1110    }
1111
1112    protected void sleep ()
1113    {
1114        // Default is to do nothing.  Users can override, but are not required to call super.
1115    }
1116
1117    protected void ensureAsleep ()
1118    {
1119        if (_isAwake) {
1120            _isAwake = false;
1121            sleep();
1122            _currentTemplateElement = null;
1123            if (isStatelessSubComponent() && !useLocalPool()) {
1124                _otherBindingsBinding = null;
1125            }
1126            // note: _parent, _componentReference, and _page are set to null in AWComponentRef (but only for stateless);
1127        }
1128    }
1129
1130    protected void hibernate ()
1131    {
1132        // Default is to do nothing.  Users can override, but are not required to call super.
1133    }
1134
1135    /**
1136     * Called when the page this component is associated with is no longer on the top
1137     * of the page cache (ie, the user has "moved off" the page).  Note that this is
1138     * only called on the "page component" (ie, the component that is the root of the
1139     * component hierarchy for this page).
1140     * @aribaapi private
1141     */
1142    protected void exit ()
1143    {
1144        //Log.aribaweb_request.debug("Exiting: %s",componentPath().toString());
1145    }
1146
1147    ///////////////////
1148    // Bindings Support
1149    ///////////////////
1150    public String[] supportedBindingNames ()
1151    {
1152        return null;
1153    }
1154
1155    public AWBindingDictionary bindings ()
1156    {
1157        return _componentReference.bindings();
1158    }
1159
1160    public AWBindingDictionary otherBindings ()
1161    {
1162        return _componentReference.otherBindings();
1163    }
1164
1165    public AWStringDictionary otherBindingsValues ()
1166    {
1167            // This merges otherBindings from the parent with other
1168            // bindings bound to this component giving preference to the
1169            // parent's bindings (allow parent to override child).
1170            // Is this *always* what we want, or do we need to allow
1171            // developers to decide how to merge things?
1172        AWStringDictionary otherBindingsValues = (AWStringDictionary)
1173            ((_otherBindingsBinding == null) ?
1174            valueForBinding(AWBindingNames.otherBindings) :
1175            valueForBinding(_otherBindingsBinding));
1176        AWBindingDictionary bindingDictionary = _componentReference.otherBindings();
1177        if (bindingDictionary != null) {
1178            if (otherBindingsValues == null) {
1179                otherBindingsValues = _page.otherBindingsValuesScratch();
1180            }
1181            for (int index = (bindingDictionary.size() - 1); index >= 0;index--) {
1182                AWEncodedString currentBindingName = bindingDictionary.nameAt(index);
1183                AWBinding currentBinding = bindingDictionary.elementAt(index);
1184                AWEncodedString currentBindingValue = encodedStringValueForBinding(currentBinding);
1185                if (currentBindingValue != null) {
1186                    otherBindingsValues.putIfIdenticalKeyAbsent(currentBindingName, currentBindingValue);
1187                }
1188            }
1189        }
1190        return otherBindingsValues;
1191    }
1192
1193    public AWBinding bindingForName (String bindingName)
1194    {
1195        // This is provided for backward compatibility -- not recommended for use.
1196        return bindingForName(bindingName, true);
1197    }
1198
1199    //public static AWMultiKeyHashtable BindingCounts = new AWMultiKeyHashtable(2);
1200    //public static int BindingCountTotal = 0;
1201
1202    public AWBinding bindingForName (String bindingName, boolean recursive)
1203    {
1204        /*
1205        BindingCountTotal++;
1206        Integer bindingCountObj = (Integer)BindingCounts.get(bindingName, componentDefinition());
1207        int bindingCount = bindingCountObj == null ? 1 : bindingCountObj.intValue() + 1;
1208        BindingCounts.put(bindingName, componentDefinition(), Constants.getInteger(bindingCount));
1209        if (BindingCountTotal % 5000 == 0) {
1210            System.out.println("***** BindingCountTotal: " + BindingCountTotal);
1211            BindingCounts.printf();
1212        }
1213        */
1214        AWBinding localBinding = _componentReference.bindingForName(bindingName, _parent);
1215        if (recursive && !hasBinding(localBinding)) {
1216            localBinding = null;
1217        }
1218        return localBinding;
1219    }
1220
1221    public boolean hasBinding (String bindingName)
1222    {
1223        return bindingForName(bindingName, true) != null;
1224    }
1225
1226    public boolean hasBinding (AWBinding binding)
1227    {
1228        return binding == null ? false : binding.bindingExistsInParentForSubcomponent(this);
1229    }
1230
1231    // Int
1232    public double doubleValueForBinding (AWBinding binding)
1233    {
1234        return (binding != null) ? binding.doubleValue(_parent) : 0.0;
1235    }
1236
1237    public double doubleValueForBinding (String bindingName)
1238    {
1239        AWBinding binding = bindingForName(bindingName, false);
1240        return doubleValueForBinding(binding);
1241    }
1242
1243    public double doubleValueForBinding (String bindingName, double defaultValue)
1244    {
1245        AWBinding binding = bindingForName(bindingName, false);
1246        return hasBinding(binding) ? doubleValueForBinding(binding) : defaultValue;
1247    }
1248
1249    // Int
1250    public int intValueForBinding (AWBinding binding)
1251    {
1252        int intValue = 0;
1253        if (binding != null) {
1254            intValue = binding.intValue(_parent);
1255        }
1256        return intValue;
1257    }
1258
1259    public int intValueForBinding (String bindingName)
1260    {
1261        AWBinding binding = bindingForName(bindingName, false);
1262        return intValueForBinding(binding);
1263    }
1264
1265    public int intValueForBinding (String bindingName, int defaultValue)
1266    {
1267        AWBinding binding = bindingForName(bindingName, false);
1268        return hasBinding(binding) ? intValueForBinding(binding) : defaultValue;
1269    }
1270
1271    // Boolean
1272    public boolean booleanValueForBinding (AWBinding binding)
1273    {
1274        return (binding != null) && binding.booleanValue(_parent);
1275    }
1276
1277    public boolean booleanValueForBinding (String bindingName)
1278    {
1279        AWBinding binding = bindingForName(bindingName, false);
1280        return booleanValueForBinding(binding);
1281    }
1282
1283    public boolean booleanValueForBinding (AWBinding binding, boolean defaultValue)
1284    {
1285        return hasBinding(binding) ? booleanValueForBinding(binding) : defaultValue;
1286    }
1287
1288    public boolean booleanValueForBinding (String bindingName, boolean defaultValue)
1289    {
1290        AWBinding binding = bindingForName(bindingName, false);
1291        return booleanValueForBinding(binding, defaultValue);
1292    }
1293
1294    // Object
1295    public Object valueForBinding (AWBinding binding)
1296    {
1297        Object objectValue = null;
1298        if (binding != null) {
1299            objectValue = binding.value(_parent);
1300        }
1301        return objectValue;
1302    }
1303
1304    public Object valueForBinding (String bindingName)
1305    {
1306        AWBinding binding = bindingForName(bindingName, false);
1307        return valueForBinding(binding);
1308    }
1309
1310    public Object valueForBinding (String bindingName, Object defaultValue)
1311    {
1312        AWBinding binding = bindingForName(bindingName, false);
1313        return hasBinding(binding) ? valueForBinding(binding) : defaultValue;
1314    }
1315
1316    // String
1317    public String stringValueForBinding (AWBinding binding)
1318    {
1319        String stringValue = null;
1320        if (binding != null) {
1321            stringValue = binding.stringValue(_parent);
1322        }
1323        return stringValue;
1324    }
1325
1326    public String stringValueForBinding (String bindingName)
1327    {
1328        AWBinding binding = bindingForName(bindingName, false);
1329        return stringValueForBinding(binding);
1330    }
1331
1332    public String stringValueForBinding (String bindingName, String defaultValue)
1333    {
1334        AWBinding binding = bindingForName(bindingName, false);
1335        return hasBinding(binding) ? stringValueForBinding(binding) : defaultValue;
1336    }
1337
1338    // AWEncodedString
1339    public AWEncodedString encodedStringValueForBinding (AWBinding binding)
1340    {
1341        AWEncodedString encodedString = null;
1342        if (binding != null) {
1343            encodedString = binding.encodedStringValue(_parent);
1344        }
1345        return encodedString;
1346    }
1347
1348    public AWEncodedString encodedStringValueForBinding (String bindingName)
1349    {
1350        AWBinding binding = bindingForName(bindingName, false);
1351        return encodedStringValueForBinding(binding);
1352    }
1353
1354    // Object
1355    public void setValueForBinding (Object objectValue, AWBinding binding)
1356    {
1357        if (binding != null) {
1358            binding.setValue(objectValue, _parent);
1359        }
1360    }
1361
1362    public void setValueForBinding (Object objectValue, String bindingName)
1363    {
1364        AWBinding binding = bindingForName(bindingName, false);
1365        setValueForBinding(objectValue, binding);
1366    }
1367
1368    // ** Int
1369    public void setValueForBinding (int intValue, AWBinding binding)
1370    {
1371        if (binding != null) {
1372            binding.setValue(intValue, _parent);
1373        }
1374    }
1375
1376    public void setValueForBinding (int intValue, String bindingName)
1377    {
1378        AWBinding binding = bindingForName(bindingName, false);
1379        setValueForBinding(intValue, binding);
1380    }
1381
1382    // ** Booelan
1383    public void setValueForBinding (boolean booleanValue, AWBinding binding)
1384    {
1385        if (binding != null) {
1386            binding.setValue(booleanValue, _parent);
1387        }
1388    }
1389
1390    public void setValueForBinding (boolean booleanValue, String bindingName)
1391    {
1392        AWBinding binding = bindingForName(bindingName, false);
1393        setValueForBinding(booleanValue, binding);
1394    }
1395
1396    ///////////////////
1397    // "xml" support
1398    ///////////////////
1399    public static void registerXmlNodeWithName (String xmlNodeName)
1400    {
1401        defaultTemplateParser().registerContainerClassForTagName(xmlNodeName, AWConcreteXmlNode.class);
1402        defaultTemplateParser().registerElementClassForTagName(xmlNodeName, AWConcreteXmlNode.class);
1403    }
1404
1405    public AWXmlNode xml ()
1406    {
1407        // Perf: recycling opportunity?
1408        return new AWXmlContext(_componentReference.xmlNode(), _parent);
1409    }
1410
1411    /////////////////
1412    // Convenience
1413    /////////////////
1414    public AWComponent pageWithName (String pageName)
1415    {
1416        return _page.requestContext().pageWithName(pageName);
1417    }
1418
1419    public AWComponent pageWithName (String pageName, Map<String, Object>assignments)
1420    {
1421        return _page.requestContext().pageWithName(pageName, assignments);
1422    }
1423
1424    public <T> T pageWithClass (Class<T> tClass)
1425    {
1426        return (T)pageWithName(tClass.getName());
1427    }
1428
1429    public <T> T pageWithClass (Class<T> tClass, Map<String, Object>assignments)
1430    {
1431        return (T)pageWithName(tClass.getName(), assignments);
1432    }
1433
1434    public Object componentConfiguration(String configName)
1435    {
1436        return _page.componentConfiguration(this, configName);
1437    }
1438
1439    public void setComponentConfiguration(String configName, Object

Large files files are truncated, but you can click here to view the full file