Coverage Report - org.apache.tapestry.AbstractComponent
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractComponent
0%
0/229
0%
0/118
2.422
 
 1  
 // Copyright 2004, 2005 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 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  
 package org.apache.tapestry;
 16  
 
 17  
 import org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.hivemind.Messages;
 19  
 import org.apache.hivemind.impl.BaseLocatable;
 20  
 import org.apache.hivemind.util.Defense;
 21  
 import org.apache.tapestry.bean.BeanProvider;
 22  
 import org.apache.tapestry.engine.IPageLoader;
 23  
 import org.apache.tapestry.event.BrowserEvent;
 24  
 import org.apache.tapestry.event.PageEvent;
 25  
 import org.apache.tapestry.internal.Component;
 26  
 import org.apache.tapestry.internal.event.IComponentEventInvoker;
 27  
 import org.apache.tapestry.listener.ListenerMap;
 28  
 import org.apache.tapestry.services.ComponentRenderWorker;
 29  
 import org.apache.tapestry.spec.IComponentSpecification;
 30  
 import org.apache.tapestry.spec.IContainedComponent;
 31  
 
 32  
 import java.util.*;
 33  
 
 34  
 /**
 35  
  * Abstract base class implementing the {@link IComponent}interface.
 36  
  * 
 37  
  * @author Howard Lewis Ship
 38  
  */
 39  
 
 40  0
 public abstract class AbstractComponent extends BaseLocatable implements IDirectEvent, Component {
 41  
     
 42  
     private static final int MAP_SIZE = 5;
 43  
     
 44  
     private static final int BODY_INIT_SIZE = 5;
 45  
 
 46  
     /**
 47  
      * Used in place of JDK 1.3's Collections.EMPTY_MAP (which is not available in JDK 1.2).
 48  
      */
 49  
 
 50  0
     private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(1));
 51  
     
 52  
     /**
 53  
      * The page that contains the component, possibly itself (if the component is in fact, a page).
 54  
      */
 55  
 
 56  
     private IPage _page;
 57  
 
 58  
     /**
 59  
      * The component which contains the component. This will only be null if the component is
 60  
      * actually a page.
 61  
      */
 62  
 
 63  
     private IComponent _container;
 64  
 
 65  
     /**
 66  
      * The simple id of this component.
 67  
      */
 68  
 
 69  
     private String _id;
 70  
     
 71  
     /**
 72  
      * The fully qualified id of this component. This is calculated the first time it is needed,
 73  
      * then cached for later.
 74  
      */
 75  
     private String _idPath;
 76  
 
 77  
     /**
 78  
      * The html tag name that was used to reference the component.
 79  
      */
 80  
     private String _templateTagName;
 81  
     
 82  
     /**
 83  
      * A {@link Map}of all bindings (for which there isn't a corresponding JavaBeans property); the
 84  
      * keys are the names of formal and informal parameters.
 85  
      */
 86  
 
 87  
     private Map _bindings;
 88  
 
 89  
     private Map _components;
 90  
 
 91  
     private INamespace _namespace;
 92  
 
 93  
     /**
 94  
      * The number of {@link IRender}objects in the body of this component.
 95  
      */
 96  
 
 97  0
     protected int _bodyCount = 0;
 98  
 
 99  
     /**
 100  
      * An aray of elements in the body of this component.
 101  
      */
 102  
 
 103  
     protected IRender[] _body;
 104  
 
 105  
     /**
 106  
      * The components' asset map.
 107  
      */
 108  
 
 109  
     private Map _assets;
 110  
 
 111  
     /**
 112  
      * A mapping that allows public instance methods to be dressed up as {@link IActionListener}
 113  
      * listener objects.
 114  
      * 
 115  
      * @since 1.0.2
 116  
      */
 117  
 
 118  
     private ListenerMap _listeners;
 119  
 
 120  
     /**
 121  
      * A bean provider; these are lazily created as needed.
 122  
      * 
 123  
      * @since 1.0.4
 124  
      */
 125  
 
 126  
     private IBeanProvider _beans;
 127  
 
 128  
     /**
 129  
      * Returns true if the component is currently rendering.
 130  
      * 
 131  
      * @see #prepareForRender(IRequestCycle)
 132  
      * @see #cleanupAfterRender(IRequestCycle)
 133  
      * @since 4.0
 134  
      */
 135  
 
 136  
     private boolean _rendering;
 137  
 
 138  
     /**
 139  
      * @since 4.0
 140  
      */
 141  
 
 142  
     private boolean _active;
 143  
 
 144  
     /** @since 4.0 */
 145  
 
 146  
     private IContainedComponent _containedComponent;
 147  
 
 148  
     private boolean _hasEvents;
 149  
     
 150  
     public void addAsset(String name, IAsset asset)
 151  
     {
 152  0
         Defense.notNull(name, "name");
 153  0
         Defense.notNull(asset, "asset");
 154  
 
 155  0
         checkActiveLock();
 156  
 
 157  0
         if (_assets == null)
 158  0
             _assets = new HashMap(MAP_SIZE);
 159  
 
 160  0
         _assets.put(name, asset);
 161  0
     }
 162  
 
 163  
     public void addComponent(IComponent component)
 164  
     {
 165  0
         Defense.notNull(component, "component");
 166  
 
 167  0
         checkActiveLock();
 168  
 
 169  0
         if (_components == null)
 170  0
             _components = new HashMap(MAP_SIZE);
 171  
         
 172  0
         _components.put(component.getId(), component);
 173  0
     }
 174  
 
 175  
     /**
 176  
      * Adds an element (which may be static text or a component) as a body element of this
 177  
      * component. Such elements are rendered by {@link #renderBody(IMarkupWriter, IRequestCycle)}.
 178  
      * 
 179  
      * @since 2.2
 180  
      */
 181  
 
 182  
     public void addBody(IRender element)
 183  
     {
 184  0
         Defense.notNull(element, "element");
 185  
         
 186  
         // Should check the specification to see if this component
 187  
         // allows body. Curently, this is checked by the component
 188  
         // in render(), which is silly.
 189  
 
 190  0
         if (_body == null)
 191  
         {
 192  0
             _body = new IRender[BODY_INIT_SIZE];
 193  0
             _body[0] = element;
 194  
 
 195  0
             _bodyCount = 1;
 196  0
             return;
 197  
         }
 198  
 
 199  
         // No more room? Make the array bigger.
 200  
 
 201  0
         if (_bodyCount == _body.length)
 202  
         {
 203  
             IRender[] newWrapped;
 204  
 
 205  0
             newWrapped = new IRender[_body.length * 2];
 206  
 
 207  0
             System.arraycopy(_body, 0, newWrapped, 0, _bodyCount);
 208  
 
 209  0
             _body = newWrapped;
 210  
         }
 211  
 
 212  0
         _body[_bodyCount++] = element;
 213  0
     }
 214  
 
 215  
     
 216  
     public IRender[] getContainedRenderers()
 217  
     {
 218  0
         return _body;
 219  
     }
 220  
 
 221  
     public IRender[] getInnerRenderers()
 222  
     {
 223  0
         return null;
 224  
     }
 225  
 
 226  
     public boolean hasEvents()
 227  
     {
 228  0
         return _hasEvents;
 229  
     }
 230  
 
 231  
     public void setHasEvents(boolean hasEvents)
 232  
     {
 233  0
         _hasEvents = hasEvents;
 234  0
     }
 235  
 
 236  
     /**
 237  
      * Invokes {@link #finishLoad()}. Subclasses may overide as needed, but must invoke this
 238  
      * implementation. {@link BaseComponent} loads its HTML template.
 239  
      */
 240  
 
 241  
     public void finishLoad(IRequestCycle cycle, IPageLoader loader, IComponentSpecification specification)
 242  
     {
 243  0
         finishLoad();
 244  0
     }
 245  
 
 246  
     /**
 247  
      * Converts informal parameters into additional attributes on the curently open tag.
 248  
      * <p>
 249  
      * Invoked from subclasses to allow additional attributes to be specified within a tag (this
 250  
      * works best when there is a one-to-one corespondence between an {@link IComponent}and a HTML
 251  
      * element.
 252  
      * <p>
 253  
      * Iterates through the bindings for this component. Filters out bindings for formal parameters.
 254  
      * <p>
 255  
      * For each acceptible key, the value is extracted using {@link IBinding#getObject()}. If the
 256  
      * value is null, no attribute is written.
 257  
      * <p>
 258  
      * If the value is an instance of {@link IAsset}, then {@link IAsset#buildURL()}
 259  
      * is invoked to convert the asset to a URL.
 260  
      * <p>
 261  
      * Finally, {@link IMarkupWriter#attribute(String,String)}is invoked with the value (or the
 262  
      * URL).
 263  
      * <p>
 264  
      * The most common use for informal parameters is to support the HTML class attribute (for use
 265  
      * with cascading style sheets) and to specify JavaScript event handlers.
 266  
      * <p>
 267  
      * Components are only required to generate attributes on the result phase; this can be skipped
 268  
      * during the rewind phase.
 269  
      */
 270  
 
 271  
     protected void renderInformalParameters(IMarkupWriter writer, IRequestCycle cycle)
 272  
     {
 273  
         String attribute;
 274  
 
 275  0
         if (_bindings == null)
 276  0
             return;
 277  
 
 278  0
         Iterator i = _bindings.entrySet().iterator();
 279  
 
 280  0
         while (i.hasNext())
 281  
         {
 282  0
             Map.Entry entry = (Map.Entry) i.next();
 283  0
             String name = (String) entry.getKey();
 284  
 
 285  0
             if (isFormalParameter(name))
 286  0
                 continue;
 287  
 
 288  0
             IBinding binding = (IBinding) entry.getValue();
 289  
 
 290  0
             Object value = binding.getObject();
 291  0
             if (value == null)
 292  0
                 continue;
 293  
 
 294  0
             if (value instanceof IAsset)
 295  
             {
 296  0
                 IAsset asset = (IAsset) value;
 297  
 
 298  
                 // Get the URL of the asset and insert that.
 299  
 
 300  0
                 attribute = asset.buildURL();
 301  0
             }
 302  
             else
 303  0
                 attribute = value.toString();
 304  
             
 305  0
             writer.attribute(name, attribute);
 306  0
         }
 307  0
     }
 308  
     
 309  
     /**
 310  
      * Renders the (unique) id attribute for this component. 
 311  
      * 
 312  
      * @param writer
 313  
      *          The writer to render attribute in.
 314  
      * @param cycle
 315  
      *          The current request.
 316  
      */
 317  
     protected void renderIdAttribute(IMarkupWriter writer, IRequestCycle cycle)
 318  
     {
 319  0
         String id = getClientId();
 320  
         
 321  0
         if (id != null)
 322  0
             writer.attribute("id", id);
 323  0
     }
 324  
     
 325  
     /** @since 4.0 */
 326  
     private boolean isFormalParameter(String name)
 327  
     {
 328  0
         Defense.notNull(name, "name");
 329  
         
 330  0
         return getSpecification().getParameter(name) != null;
 331  
     }
 332  
     
 333  
     /**
 334  
      * Returns the named binding, or null if it doesn't exist.
 335  
      * <p>
 336  
      * In Tapestry 3.0, it was possible to force a binding to be stored in a component property by
 337  
      * defining a concrete or abstract property named "nameBinding" of type {@link IBinding}. This
 338  
      * has been removed in release 4.0 and bindings are always stored inside a Map of the component.
 339  
      * 
 340  
      * @see #setBinding(String,IBinding)
 341  
      */
 342  
 
 343  
     public IBinding getBinding(String name)
 344  
     {
 345  0
         Defense.notNull(name, "name");
 346  
 
 347  0
         if (_bindings == null)
 348  0
             return null;
 349  
 
 350  0
         return (IBinding) _bindings.get(name);
 351  
     }
 352  
 
 353  
     /**
 354  
      * Returns true if the specified parameter is bound.
 355  
      * 
 356  
      * @since 4.0
 357  
      */
 358  
 
 359  
     public boolean isParameterBound(String parameterName)
 360  
     {
 361  0
         Defense.notNull(parameterName, "parameterName");
 362  
 
 363  0
         return _bindings != null && _bindings.containsKey(parameterName);
 364  
     }
 365  
 
 366  
     public IComponent getComponent(String id)
 367  
     {
 368  0
         Defense.notNull(id, "id");
 369  
 
 370  0
         IComponent result = null;
 371  
 
 372  0
         if (_components != null)
 373  0
             result = (IComponent) _components.get(id);
 374  
 
 375  0
         if (result == null)
 376  0
             throw new ApplicationRuntimeException(Tapestry.format("no-such-component", this, id),
 377  
                     this, null, null);
 378  
 
 379  0
         return result;
 380  
     }
 381  
 
 382  
     public IComponent getContainer()
 383  
     {
 384  0
         return _container;
 385  
     }
 386  
 
 387  
     public void setContainer(IComponent value)
 388  
     {
 389  0
         checkActiveLock();
 390  
 
 391  0
         if (_container != null)
 392  0
             throw new ApplicationRuntimeException(Tapestry
 393  
                     .getMessage("AbstractComponent.attempt-to-change-container"));
 394  
 
 395  0
         _container = value;
 396  0
     }
 397  
 
 398  
     /**
 399  
      * Returns the name of the page, a slash, and this component's id path. Pages are different,
 400  
      * they override this method to simply return their page name.
 401  
      * 
 402  
      * @see #getIdPath()
 403  
      */
 404  
 
 405  
     public String getExtendedId()
 406  
     {
 407  0
         if (_page == null)
 408  0
             return null;
 409  
         
 410  0
         return _page.getPageName() + "/" + getIdPath();
 411  
     }
 412  
 
 413  
     /** @since 4.1 */
 414  
     
 415  
     public String getSpecifiedId()
 416  
     {
 417  0
         String id = getBoundId();
 418  
         
 419  0
         if (id != null)
 420  0
             return id;
 421  
         
 422  0
         return getId();
 423  
     }
 424  
     
 425  
     public String getId()
 426  
     {
 427  0
         return _id;
 428  
     }
 429  
 
 430  
     public void setId(String value)
 431  
     {
 432  0
         if (_id != null)
 433  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("AbstractComponent.attempt-to-change-component-id"));
 434  
 
 435  0
         _id = value;
 436  0
     }
 437  
     
 438  
     public String getIdPath()
 439  
     {
 440  0
         if (_idPath != null)
 441  0
             return _idPath;
 442  
         
 443  
         String containerIdPath;
 444  
         
 445  0
         if (_container == null)
 446  0
             throw new NullPointerException(Tapestry.format("AbstractComponent.null-container", this));
 447  
         
 448  0
         containerIdPath = _container.getIdPath();
 449  
         
 450  0
         if (containerIdPath == null)
 451  0
             _idPath = _id;
 452  
         else
 453  0
             _idPath = containerIdPath + "." + _id;
 454  
 
 455  0
         return _idPath;
 456  
     }
 457  
     
 458  
     /**
 459  
      * {@inheritDoc}
 460  
      * @since 4.1
 461  
      */
 462  
     public abstract String getClientId();
 463  
     
 464  
     public abstract void setClientId(String id);
 465  
     
 466  
     /**
 467  
      * {@inheritDoc}
 468  
      */
 469  
     public String peekClientId()
 470  
     {
 471  0
         if (getPage() == null)
 472  0
             return null;
 473  
         
 474  0
         String id = getSpecifiedId();
 475  0
         if (id == null)
 476  0
             return null;
 477  
         
 478  0
         return getPage().getRequestCycle().peekUniqueId(TapestryUtils.convertTapestryIdToNMToken(id));
 479  
     }
 480  
     
 481  
     protected void generateClientId()
 482  
     {
 483  0
         String id = getSpecifiedId();
 484  
         
 485  0
         if (id != null && getPage() != null && getPage().getRequestCycle() != null)
 486  0
              setClientId(getPage().getRequestCycle().getUniqueId(TapestryUtils.convertTapestryIdToNMToken(id)));
 487  0
     }
 488  
     
 489  
     protected String getBoundId()
 490  
     {
 491  0
         if (_bindings == null)
 492  0
             return null;
 493  
         
 494  0
         IBinding id = (IBinding)_bindings.get("id");
 495  
         
 496  0
         if (id == null || id.getObject() == null)
 497  0
             return null;
 498  
         
 499  0
         return id.getObject().toString();
 500  
     }
 501  
     
 502  
     public String getTemplateTagName()
 503  
     {
 504  0
         return _templateTagName;
 505  
     }
 506  
     
 507  
     /** 
 508  
      * {@inheritDoc}
 509  
      */
 510  
     public void setTemplateTagName(String tag)
 511  
     {
 512  0
         if (_templateTagName != null)
 513  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("AbstractComponent.attempt-to-change-template-tag"));
 514  
         
 515  0
         _templateTagName = tag;
 516  0
     }
 517  
 
 518  
     public IPage getPage()
 519  
     {
 520  0
         return _page;
 521  
     }
 522  
 
 523  
     public void setPage(IPage value)
 524  
     {
 525  0
         if (_page != null)
 526  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("AbstractComponent.attempt-to-change-page"));
 527  
 
 528  0
         _page = value;
 529  0
     }
 530  
 
 531  
     /**
 532  
      * Renders all elements wrapped by the receiver.
 533  
      */
 534  
 
 535  
     public void renderBody(IMarkupWriter writer, IRequestCycle cycle)
 536  
     {
 537  0
         for (int i = 0; i < _bodyCount; i++)
 538  0
             cycle.getResponseBuilder().render(writer, _body[i], cycle);
 539  0
     }
 540  
 
 541  
     /**
 542  
      * Adds the binding with the given name, replacing any existing binding with that name.
 543  
      * <p>
 544  
      * 
 545  
      * @see #getBinding(String)
 546  
      */
 547  
 
 548  
     public void setBinding(String name, IBinding binding)
 549  
     {
 550  0
         Defense.notNull(name, "name");
 551  0
         Defense.notNull(binding, "binding");
 552  
 
 553  0
         if (_bindings == null)
 554  0
             _bindings = new HashMap(MAP_SIZE);
 555  
 
 556  0
         _bindings.put(name, binding);
 557  0
     }
 558  
 
 559  
     public String toString()
 560  
     {
 561  
         StringBuffer buffer;
 562  
         
 563  0
         buffer = new StringBuffer(super.toString());
 564  
 
 565  0
         buffer.append('[');
 566  
 
 567  0
         buffer.append(getExtendedId());
 568  
 
 569  0
         buffer.append(']');
 570  
 
 571  0
         return buffer.toString();
 572  
     }
 573  
 
 574  
     /**
 575  
      * Returns an unmodifiable {@link Map}of components, keyed on component id. Never returns null,
 576  
      * but may return an empty map. The returned map is immutable.
 577  
      */
 578  
 
 579  
     public Map getComponents()
 580  
     {
 581  0
         if (_components == null)
 582  0
             return EMPTY_MAP;
 583  
 
 584  0
         return _components;
 585  
 
 586  
     }
 587  
 
 588  
     public Map getAssets()
 589  
     {
 590  0
         if (_assets == null)
 591  0
             return EMPTY_MAP;
 592  
 
 593  0
         return _assets;
 594  
     }
 595  
 
 596  
     public IAsset getAsset(String name)
 597  
     {
 598  0
         if (_assets == null)
 599  0
             return null;
 600  
 
 601  0
         return (IAsset) _assets.get(name);
 602  
     }
 603  
 
 604  
     public Collection getBindingNames()
 605  
     {
 606  
         // If no conainer, i.e. a page, then no bindings.
 607  
 
 608  0
         if (_container == null)
 609  0
             return null;
 610  
 
 611  0
         HashSet result = new HashSet();
 612  
 
 613  
         // All the informal bindings go into the bindings Map.
 614  
 
 615  0
         if (_bindings != null)
 616  0
             result.addAll(_bindings.keySet());
 617  
 
 618  
         // Now, iterate over the formal parameters and add the formal parameters
 619  
         // that have a binding.
 620  
 
 621  0
         List names = getSpecification().getParameterNames();
 622  
 
 623  0
         int count = names.size();
 624  
 
 625  0
         for (int i = 0; i < count; i++)
 626  
         {
 627  0
             String name = (String) names.get(i);
 628  
 
 629  0
             if (result.contains(name))
 630  0
                 continue;
 631  
 
 632  0
             if (getBinding(name) != null)
 633  0
                 result.add(name);
 634  
         }
 635  
 
 636  0
         return result;
 637  
     }
 638  
 
 639  
     /**
 640  
      * Returns an unmodifiable {@link Map}of all bindings for this component.
 641  
      * 
 642  
      * @since 1.0.5
 643  
      */
 644  
 
 645  
     public Map getBindings()
 646  
     {
 647  0
         if (_bindings == null)
 648  0
             return EMPTY_MAP;
 649  
 
 650  0
         return _bindings;
 651  
     }
 652  
 
 653  
     /**
 654  
      * Returns a {@link ListenerMap}&nbsp;for the component. A ListenerMap contains a number of
 655  
      * synthetic read-only properties that implement the {@link IActionListener}interface, but in
 656  
      * fact, cause public instance methods to be invoked.
 657  
      * 
 658  
      * @since 1.0.2
 659  
      */
 660  
 
 661  
     public ListenerMap getListeners()
 662  
     {
 663  
         // This is what's called a violation of the Law of Demeter!
 664  
         // This should probably be converted over to some kind of injection, as with
 665  
         // getMessages(), etc.
 666  
 
 667  0
         if (_listeners == null)
 668  0
             _listeners = getPage().getEngine().getInfrastructure().getListenerMapSource().getListenerMapForObject(this);
 669  
 
 670  0
         return _listeners;
 671  
     }
 672  
 
 673  
     /**
 674  
      * Returns the {@link IBeanProvider}for this component. This is lazily created the first time
 675  
      * it is needed.
 676  
      * 
 677  
      * @since 1.0.4
 678  
      */
 679  
 
 680  
     public IBeanProvider getBeans()
 681  
     {
 682  0
         if (_beans == null)
 683  0
             _beans = new BeanProvider(this);
 684  
 
 685  0
         return _beans;
 686  
     }
 687  
 
 688  
     /**
 689  
      * Invoked, as a convienience, from
 690  
      * {@link #finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}. This implemenation
 691  
      * does nothing. Subclasses may override without invoking this implementation.
 692  
      * 
 693  
      * @since 1.0.5
 694  
      */
 695  
 
 696  
     protected void finishLoad()
 697  
     {
 698  0
     }
 699  
 
 700  
     /**
 701  
      * The main method used to render the component. Invokes
 702  
      * {@link #prepareForRender(IRequestCycle)}, then
 703  
      * {@link #renderComponent(IMarkupWriter, IRequestCycle)}.
 704  
      * {@link #cleanupAfterRender(IRequestCycle)}is invoked in a <code>finally</code> block.
 705  
      * <p>
 706  
      * Subclasses should not override this method; instead they will implement
 707  
      * {@link #renderComponent(IMarkupWriter, IRequestCycle)}.
 708  
      * 
 709  
      * @since 2.0.3
 710  
      */
 711  
 
 712  
     public final void render(IMarkupWriter writer, IRequestCycle cycle)
 713  
     {
 714  
         try
 715  
         {
 716  0
             _rendering = true;
 717  
             
 718  0
             cycle.renderStackPush(this);
 719  
             
 720  0
             generateClientId();
 721  
             
 722  0
             prepareForRender(cycle);
 723  
             
 724  0
             renderComponent(writer, cycle);
 725  
         }
 726  
         finally
 727  
         {
 728  0
             _rendering = false;
 729  
             
 730  0
             cleanupAfterRender(cycle);
 731  
             
 732  0
             cycle.renderStackPop();
 733  0
         }
 734  0
     }
 735  
 
 736  
     /**
 737  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}to prepare the component to render.
 738  
      * This implementation sets JavaBeans properties from matching bound parameters. The default
 739  
      * implementation of this method is empty.
 740  
      * 
 741  
      * @since 2.0.3
 742  
      */
 743  
 
 744  
     protected void prepareForRender(IRequestCycle cycle)
 745  
     {
 746  0
     }
 747  
 
 748  
     /**
 749  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}to actually render the component
 750  
      * (with any parameter values already set). This is the method that subclasses must implement.
 751  
      * 
 752  
      * @since 2.0.3
 753  
      */
 754  
 
 755  
     protected abstract void renderComponent(IMarkupWriter writer, IRequestCycle cycle);
 756  
     
 757  
     /**
 758  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}after the component renders.
 759  
      * 
 760  
      * @since 2.0.3
 761  
      */
 762  
 
 763  
     protected void cleanupAfterRender(IRequestCycle cycle)
 764  
     {
 765  0
         getRenderWorker().renderComponent(cycle, this);
 766  0
     }
 767  
 
 768  
     public INamespace getNamespace()
 769  
     {
 770  0
         return _namespace;
 771  
     }
 772  
 
 773  
     public void setNamespace(INamespace namespace)
 774  
     {
 775  0
         _namespace = namespace;
 776  0
     }
 777  
 
 778  
     /**
 779  
      * Returns the body of the component, the element (which may be static HTML or components) that
 780  
      * the component immediately wraps. May return null. Do not modify the returned array. The array
 781  
      * may be padded with nulls.
 782  
      * 
 783  
      * @since 2.3
 784  
      * @see #getBodyCount()
 785  
      */
 786  
 
 787  
     public IRender[] getBody()
 788  
     {
 789  0
         return _body;
 790  
     }
 791  
 
 792  
     /**
 793  
      * Returns the active number of elements in the the body, which may be zero.
 794  
      * 
 795  
      * @since 2.3
 796  
      * @see #getBody()
 797  
      */
 798  
 
 799  
     public int getBodyCount()
 800  
     {
 801  0
         return _bodyCount;
 802  
     }
 803  
 
 804  
     /**
 805  
      * Empty implementation of
 806  
      * {@link org.apache.tapestry.event.PageRenderListener#pageEndRender(PageEvent)}. This allows
 807  
      * classes to implement {@link org.apache.tapestry.event.PageRenderListener}and only implement
 808  
      * the {@link org.apache.tapestry.event.PageRenderListener#pageBeginRender(PageEvent)}method.
 809  
      * 
 810  
      * @since 3.0
 811  
      */
 812  
 
 813  
     public void pageEndRender(PageEvent event)
 814  
     {
 815  0
     }
 816  
 
 817  
     /**
 818  
      * @since 4.0
 819  
      */
 820  
 
 821  
     public final boolean isRendering()
 822  
     {
 823  0
         return _rendering;
 824  
     }
 825  
 
 826  
     /**
 827  
      * Returns true if the component has been transitioned into its active state by invoking
 828  
      * {@link #enterActiveState()}.
 829  
      * 
 830  
      * @since 4.0
 831  
      */
 832  
 
 833  
     protected final boolean isInActiveState()
 834  
     {
 835  0
         return _active;
 836  
     }
 837  
 
 838  
     /** @since 4.0 */
 839  
     public final void enterActiveState()
 840  
     {
 841  0
         _active = true;
 842  0
     }
 843  
 
 844  
     /** @since 4.0 */
 845  
 
 846  
     protected final void checkActiveLock()
 847  
     {
 848  0
         if (_active)
 849  0
             throw new UnsupportedOperationException(TapestryMessages.componentIsLocked(this));
 850  0
     }
 851  
 
 852  
     public Messages getMessages()
 853  
     {
 854  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getMessages"));
 855  
     }
 856  
 
 857  
     public IComponentSpecification getSpecification()
 858  
     {
 859  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getSpecification"));
 860  
     }
 861  
 
 862  
     /** @since 4.0 */
 863  
     public final IContainedComponent getContainedComponent()
 864  
     {
 865  0
         return _containedComponent;
 866  
     }
 867  
 
 868  
     /** @since 4.0 */
 869  
     public final void setContainedComponent(IContainedComponent containedComponent)
 870  
     {
 871  0
         Defense.notNull(containedComponent, "containedComponent");
 872  
 
 873  0
         if (_containedComponent != null)
 874  0
             throw new ApplicationRuntimeException(TapestryMessages
 875  
                     .attemptToChangeContainedComponent(this));
 876  
 
 877  0
         _containedComponent = containedComponent;
 878  0
     }
 879  
     
 880  
     /**
 881  
      * {@inheritDoc}
 882  
      */
 883  
     public IComponentEventInvoker getEventInvoker()
 884  
     {
 885  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getEventInvoker"));
 886  
     }
 887  
     
 888  
     /**
 889  
      * {@inheritDoc}
 890  
      */
 891  
     public void triggerEvent(IRequestCycle cycle, BrowserEvent event)
 892  
     {
 893  0
         getEventInvoker().invokeListeners(this, cycle, event);
 894  0
     }
 895  
     
 896  
     public ComponentRenderWorker getRenderWorker()
 897  
     {
 898  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getRenderWorker"));
 899  
     }
 900  
     
 901  
     /**
 902  
      * {@inheritDoc}
 903  
      */
 904  
     public boolean isStateful()
 905  
     {
 906  0
         return false;
 907  
     }
 908  
     
 909  
     /**
 910  
      * {@inheritDoc}
 911  
      */
 912  
     public int hashCode()
 913  
     {
 914  0
         final int prime = 31;
 915  0
         int result = 1;
 916  0
         result = prime * result + ((getClientId() == null) ? 0 : getClientId().hashCode());
 917  0
         result = prime * result + ((_id == null) ? 0 : _id.hashCode());
 918  0
         return result;
 919  
     }
 920  
     
 921  
     /**
 922  
      * {@inheritDoc}
 923  
      */
 924  
     public boolean equals(Object obj)
 925  
     {
 926  0
         if (this == obj) return true;
 927  0
         if (obj == null) return false;
 928  0
         if (getClass() != obj.getClass()) return false;
 929  0
         final AbstractComponent other = (AbstractComponent) obj;
 930  0
         if (getClientId() == null) {
 931  0
             if (other.getClientId() != null) return false;
 932  0
         } else if (!getClientId().equals(other.getClientId())) return false;
 933  0
         if (_id == null) {
 934  0
             if (other._id != null) return false;
 935  0
         } else if (!_id.equals(other._id)) return false;
 936  0
         return true;
 937  
     }
 938  
 }