Coverage Report - org.apache.tapestry.AbstractComponent
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractComponent
57% 
62% 
2.328
 
 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  222
 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  1
     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  222
     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  36
         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  36
         if (_body == null)
 191  
         {
 192  36
             _body = new IRender[BODY_INIT_SIZE];
 193  36
             _body[0] = element;
 194  
 
 195  36
             _bodyCount = 1;
 196  36
             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  5
         finishLoad();
 244  5
     }
 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  54
         if (_bindings == null)
 276  41
             return;
 277  
 
 278  13
         Iterator i = _bindings.entrySet().iterator();
 279  
 
 280  27
         while (i.hasNext())
 281  
         {
 282  14
             Map.Entry entry = (Map.Entry) i.next();
 283  14
             String name = (String) entry.getKey();
 284  
 
 285  14
             if (isFormalParameter(name))
 286  2
                 continue;
 287  
 
 288  12
             IBinding binding = (IBinding) entry.getValue();
 289  
 
 290  12
             Object value = binding.getObject();
 291  12
             if (value == null)
 292  0
                 continue;
 293  
 
 294  12
             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  12
                 attribute = value.toString();
 304  
             
 305  12
             writer.attribute(name, attribute);
 306  12
         }
 307  13
     }
 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  42
         String id = getClientId();
 320  
         
 321  42
         if (id != null)
 322  34
             writer.attribute("id", id);
 323  42
     }
 324  
     
 325  
     /** @since 4.0 */
 326  
     private boolean isFormalParameter(String name)
 327  
     {
 328  14
         Defense.notNull(name, "name");
 329  
         
 330  14
         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  30
         Defense.notNull(name, "name");
 346  
 
 347  30
         if (_bindings == null)
 348  11
             return null;
 349  
 
 350  19
         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  30
         Defense.notNull(parameterName, "parameterName");
 362  
 
 363  30
         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  6
         return _container;
 385  
     }
 386  
 
 387  
     public void setContainer(IComponent value)
 388  
     {
 389  13
         checkActiveLock();
 390  
 
 391  13
         if (_container != null)
 392  0
             throw new ApplicationRuntimeException(Tapestry
 393  
                     .getMessage("AbstractComponent.attempt-to-change-container"));
 394  
 
 395  13
         _container = value;
 396  13
     }
 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  8
         if (_page == null)
 408  3
             return null;
 409  
         
 410  5
         return _page.getPageName() + "/" + getIdPath();
 411  
     }
 412  
 
 413  
     /** @since 4.1 */
 414  
     
 415  
     public String getSpecifiedId()
 416  
     {
 417  65
         String id = getBoundId();
 418  
         
 419  65
         if (id != null)
 420  0
             return id;
 421  
         
 422  65
         return getId();
 423  
     }
 424  
     
 425  
     public String getId()
 426  
     {
 427  67
         return _id;
 428  
     }
 429  
 
 430  
     public void setId(String value)
 431  
     {
 432  16
         if (_id != null)
 433  0
             throw new ApplicationRuntimeException(Tapestry
 434  
                     .getMessage("AbstractComponent.attempt-to-change-component-id"));
 435  
 
 436  16
         _id = value;
 437  16
     }
 438  
     
 439  
     public String getIdPath()
 440  
     {
 441  5
         if (_idPath != null)
 442  0
             return _idPath;
 443  
         
 444  
         String containerIdPath;
 445  
         
 446  5
         if (_container == null)
 447  0
             throw new NullPointerException(Tapestry.format("AbstractComponent.null-container", this));
 448  
         
 449  5
         containerIdPath = _container.getIdPath();
 450  
         
 451  5
         if (containerIdPath == null)
 452  5
             _idPath = _id;
 453  
         else
 454  0
             _idPath = containerIdPath + "." + _id;
 455  
 
 456  5
         return _idPath;
 457  
     }
 458  
     
 459  
     /**
 460  
      * {@inheritDoc}
 461  
      * @since 4.1
 462  
      */
 463  
     public abstract String getClientId();
 464  
     
 465  
     public abstract void setClientId(String id);
 466  
     
 467  
     /**
 468  
      * {@inheritDoc}
 469  
      */
 470  
     public String peekClientId()
 471  
     {
 472  0
         if (getPage() == null)
 473  0
             return null;
 474  
         
 475  0
         String id = getSpecifiedId();
 476  0
         if (id == null)
 477  0
             return null;
 478  
         
 479  0
         return getPage().getRequestCycle().peekUniqueId(TapestryUtils.convertTapestryIdToNMToken(id));
 480  
     }
 481  
     
 482  
     protected void generateClientId()
 483  
     {
 484  50
         String id = getSpecifiedId();
 485  
         
 486  50
         if (id != null && getPage() != null && getPage().getRequestCycle() != null)
 487  0
              setClientId(getPage().getRequestCycle().getUniqueId(TapestryUtils.convertTapestryIdToNMToken(id)));
 488  50
     }
 489  
     
 490  
     protected String getBoundId()
 491  
     {
 492  65
         if (_bindings == null)
 493  53
             return null;
 494  
         
 495  12
         IBinding id = (IBinding)_bindings.get("id");
 496  
         
 497  12
         if (id == null || id.getObject() == null)
 498  12
             return null;
 499  
         
 500  0
         return id.getObject().toString();
 501  
     }
 502  
     
 503  
     public String getTemplateTagName()
 504  
     {
 505  19
         return _templateTagName;
 506  
     }
 507  
     
 508  
     /** 
 509  
      * {@inheritDoc}
 510  
      */
 511  
     public void setTemplateTagName(String tag)
 512  
     {
 513  12
         if (_templateTagName != null)
 514  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("AbstractComponent.attempt-to-change-template-tag"));
 515  
         
 516  12
         _templateTagName = tag;
 517  12
     }
 518  
 
 519  
     public IPage getPage()
 520  
     {
 521  53
         return _page;
 522  
     }
 523  
 
 524  
     public void setPage(IPage value)
 525  
     {
 526  30
         if (_page != null)
 527  0
             throw new ApplicationRuntimeException(Tapestry.getMessage("AbstractComponent.attempt-to-change-page"));
 528  
 
 529  30
         _page = value;
 530  30
     }
 531  
 
 532  
     /**
 533  
      * Renders all elements wrapped by the receiver.
 534  
      */
 535  
 
 536  
     public void renderBody(IMarkupWriter writer, IRequestCycle cycle)
 537  
     {
 538  61
         for (int i = 0; i < _bodyCount; i++)
 539  27
             cycle.getResponseBuilder().render(writer, _body[i], cycle);
 540  34
     }
 541  
 
 542  
     /**
 543  
      * Adds the binding with the given name, replacing any existing binding with that name.
 544  
      * <p>
 545  
      * 
 546  
      * @see #getBinding(String)
 547  
      */
 548  
 
 549  
     public void setBinding(String name, IBinding binding)
 550  
     {
 551  24
         Defense.notNull(name, "name");
 552  24
         Defense.notNull(binding, "binding");
 553  
 
 554  24
         if (_bindings == null)
 555  22
             _bindings = new HashMap(MAP_SIZE);
 556  
 
 557  24
         _bindings.put(name, binding);
 558  24
     }
 559  
 
 560  
     public String toString()
 561  
     {
 562  
         StringBuffer buffer;
 563  
         
 564  0
         buffer = new StringBuffer(super.toString());
 565  
 
 566  0
         buffer.append('[');
 567  
 
 568  0
         buffer.append(getExtendedId());
 569  
 
 570  0
         buffer.append(']');
 571  
 
 572  0
         return buffer.toString();
 573  
     }
 574  
 
 575  
     /**
 576  
      * Returns an unmodifiable {@link Map}of components, keyed on component id. Never returns null,
 577  
      * but may return an empty map. The returned map is immutable.
 578  
      */
 579  
 
 580  
     public Map getComponents()
 581  
     {
 582  0
         if (_components == null)
 583  0
             return EMPTY_MAP;
 584  
 
 585  0
         return _components;
 586  
 
 587  
     }
 588  
 
 589  
     public Map getAssets()
 590  
     {
 591  0
         if (_assets == null)
 592  0
             return EMPTY_MAP;
 593  
 
 594  0
         return _assets;
 595  
     }
 596  
 
 597  
     public IAsset getAsset(String name)
 598  
     {
 599  0
         if (_assets == null)
 600  0
             return null;
 601  
 
 602  0
         return (IAsset) _assets.get(name);
 603  
     }
 604  
 
 605  
     public Collection getBindingNames()
 606  
     {
 607  
         // If no conainer, i.e. a page, then no bindings.
 608  
 
 609  7
         if (_container == null)
 610  0
             return null;
 611  
 
 612  7
         HashSet result = new HashSet();
 613  
 
 614  
         // All the informal bindings go into the bindings Map.
 615  
 
 616  7
         if (_bindings != null)
 617  1
             result.addAll(_bindings.keySet());
 618  
 
 619  
         // Now, iterate over the formal parameters and add the formal parameters
 620  
         // that have a binding.
 621  
 
 622  7
         List names = getSpecification().getParameterNames();
 623  
 
 624  7
         int count = names.size();
 625  
 
 626  7
         for (int i = 0; i < count; i++)
 627  
         {
 628  0
             String name = (String) names.get(i);
 629  
 
 630  0
             if (result.contains(name))
 631  0
                 continue;
 632  
 
 633  0
             if (getBinding(name) != null)
 634  0
                 result.add(name);
 635  
         }
 636  
 
 637  7
         return result;
 638  
     }
 639  
 
 640  
     /**
 641  
      * Returns an unmodifiable {@link Map}of all bindings for this component.
 642  
      * 
 643  
      * @since 1.0.5
 644  
      */
 645  
 
 646  
     public Map getBindings()
 647  
     {
 648  0
         if (_bindings == null)
 649  0
             return EMPTY_MAP;
 650  
 
 651  0
         return _bindings;
 652  
     }
 653  
 
 654  
     /**
 655  
      * Returns a {@link ListenerMap}&nbsp;for the component. A ListenerMap contains a number of
 656  
      * synthetic read-only properties that implement the {@link IActionListener}interface, but in
 657  
      * fact, cause public instance methods to be invoked.
 658  
      * 
 659  
      * @since 1.0.2
 660  
      */
 661  
 
 662  
     public ListenerMap getListeners()
 663  
     {
 664  
         // This is what's called a violation of the Law of Demeter!
 665  
         // This should probably be converted over to some kind of injection, as with
 666  
         // getMessages(), etc.
 667  
 
 668  0
         if (_listeners == null)
 669  0
             _listeners = getPage().getEngine().getInfrastructure().getListenerMapSource().getListenerMapForObject(this);
 670  
 
 671  0
         return _listeners;
 672  
     }
 673  
 
 674  
     /**
 675  
      * Returns the {@link IBeanProvider}for this component. This is lazily created the first time
 676  
      * it is needed.
 677  
      * 
 678  
      * @since 1.0.4
 679  
      */
 680  
 
 681  
     public IBeanProvider getBeans()
 682  
     {
 683  0
         if (_beans == null)
 684  0
             _beans = new BeanProvider(this);
 685  
 
 686  0
         return _beans;
 687  
     }
 688  
 
 689  
     /**
 690  
      * Invoked, as a convienience, from
 691  
      * {@link #finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}. This implemenation
 692  
      * does nothing. Subclasses may override without invoking this implementation.
 693  
      * 
 694  
      * @since 1.0.5
 695  
      */
 696  
 
 697  
     protected void finishLoad()
 698  
     {
 699  2
     }
 700  
 
 701  
     /**
 702  
      * The main method used to render the component. Invokes
 703  
      * {@link #prepareForRender(IRequestCycle)}, then
 704  
      * {@link #renderComponent(IMarkupWriter, IRequestCycle)}.
 705  
      * {@link #cleanupAfterRender(IRequestCycle)}is invoked in a <code>finally</code> block.
 706  
      * <p>
 707  
      * Subclasses should not override this method; instead they will implement
 708  
      * {@link #renderComponent(IMarkupWriter, IRequestCycle)}.
 709  
      * 
 710  
      * @since 2.0.3
 711  
      */
 712  
 
 713  
     public final void render(IMarkupWriter writer, IRequestCycle cycle)
 714  
     {
 715  
         try
 716  
         {
 717  98
             _rendering = true;
 718  
             
 719  98
             cycle.renderStackPush(this);
 720  
             
 721  98
             generateClientId();
 722  
             
 723  98
             prepareForRender(cycle);
 724  
             
 725  98
             renderComponent(writer, cycle);
 726  
         }
 727  
         finally
 728  
         {
 729  98
             _rendering = false;
 730  
             
 731  98
             cleanupAfterRender(cycle);
 732  
             
 733  98
             cycle.renderStackPop();
 734  89
         }
 735  89
     }
 736  
 
 737  
     /**
 738  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}to prepare the component to render.
 739  
      * This implementation sets JavaBeans properties from matching bound parameters. The default
 740  
      * implementation of this method is empty.
 741  
      * 
 742  
      * @since 2.0.3
 743  
      */
 744  
 
 745  
     protected void prepareForRender(IRequestCycle cycle)
 746  
     {
 747  98
     }
 748  
 
 749  
     /**
 750  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}to actually render the component
 751  
      * (with any parameter values already set). This is the method that subclasses must implement.
 752  
      * 
 753  
      * @since 2.0.3
 754  
      */
 755  
 
 756  
     protected abstract void renderComponent(IMarkupWriter writer, IRequestCycle cycle);
 757  
     
 758  
     /**
 759  
      * Invoked by {@link #render(IMarkupWriter, IRequestCycle)}after the component renders.
 760  
      * 
 761  
      * @since 2.0.3
 762  
      */
 763  
 
 764  
     protected void cleanupAfterRender(IRequestCycle cycle)
 765  
     {
 766  95
         getRenderWorker().renderComponent(cycle, this);
 767  95
     }
 768  
 
 769  
     public INamespace getNamespace()
 770  
     {
 771  114
         return _namespace;
 772  
     }
 773  
 
 774  
     public void setNamespace(INamespace namespace)
 775  
     {
 776  20
         _namespace = namespace;
 777  20
     }
 778  
 
 779  
     /**
 780  
      * Returns the body of the component, the element (which may be static HTML or components) that
 781  
      * the component immediately wraps. May return null. Do not modify the returned array. The array
 782  
      * may be padded with nulls.
 783  
      * 
 784  
      * @since 2.3
 785  
      * @see #getBodyCount()
 786  
      */
 787  
 
 788  
     public IRender[] getBody()
 789  
     {
 790  0
         return _body;
 791  
     }
 792  
 
 793  
     /**
 794  
      * Returns the active number of elements in the the body, which may be zero.
 795  
      * 
 796  
      * @since 2.3
 797  
      * @see #getBody()
 798  
      */
 799  
 
 800  
     public int getBodyCount()
 801  
     {
 802  0
         return _bodyCount;
 803  
     }
 804  
 
 805  
     /**
 806  
      * Empty implementation of
 807  
      * {@link org.apache.tapestry.event.PageRenderListener#pageEndRender(PageEvent)}. This allows
 808  
      * classes to implement {@link org.apache.tapestry.event.PageRenderListener}and only implement
 809  
      * the {@link org.apache.tapestry.event.PageRenderListener#pageBeginRender(PageEvent)}method.
 810  
      * 
 811  
      * @since 3.0
 812  
      */
 813  
 
 814  
     public void pageEndRender(PageEvent event)
 815  
     {
 816  0
     }
 817  
 
 818  
     /**
 819  
      * @since 4.0
 820  
      */
 821  
 
 822  
     public final boolean isRendering()
 823  
     {
 824  4
         return _rendering;
 825  
     }
 826  
 
 827  
     /**
 828  
      * Returns true if the component has been transitioned into its active state by invoking
 829  
      * {@link #enterActiveState()}.
 830  
      * 
 831  
      * @since 4.0
 832  
      */
 833  
 
 834  
     protected final boolean isInActiveState()
 835  
     {
 836  0
         return _active;
 837  
     }
 838  
 
 839  
     /** @since 4.0 */
 840  
     public final void enterActiveState()
 841  
     {
 842  0
         _active = true;
 843  0
     }
 844  
 
 845  
     /** @since 4.0 */
 846  
 
 847  
     protected final void checkActiveLock()
 848  
     {
 849  13
         if (_active)
 850  0
             throw new UnsupportedOperationException(TapestryMessages.componentIsLocked(this));
 851  13
     }
 852  
 
 853  
     public Messages getMessages()
 854  
     {
 855  1
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getMessages"));
 856  
     }
 857  
 
 858  
     public IComponentSpecification getSpecification()
 859  
     {
 860  1
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getSpecification"));
 861  
     }
 862  
 
 863  
     /** @since 4.0 */
 864  
     public final IContainedComponent getContainedComponent()
 865  
     {
 866  1
         return _containedComponent;
 867  
     }
 868  
 
 869  
     /** @since 4.0 */
 870  
     public final void setContainedComponent(IContainedComponent containedComponent)
 871  
     {
 872  3
         Defense.notNull(containedComponent, "containedComponent");
 873  
 
 874  3
         if (_containedComponent != null)
 875  1
             throw new ApplicationRuntimeException(TapestryMessages
 876  
                     .attemptToChangeContainedComponent(this));
 877  
 
 878  2
         _containedComponent = containedComponent;
 879  2
     }
 880  
     
 881  
     /**
 882  
      * {@inheritDoc}
 883  
      */
 884  
     public IComponentEventInvoker getEventInvoker()
 885  
     {
 886  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getEventInvoker"));
 887  
     }
 888  
     
 889  
     /**
 890  
      * {@inheritDoc}
 891  
      */
 892  
     public void triggerEvent(IRequestCycle cycle, BrowserEvent event)
 893  
     {
 894  0
         getEventInvoker().invokeListeners(this, cycle, event);
 895  0
     }
 896  
     
 897  
     public ComponentRenderWorker getRenderWorker()
 898  
     {
 899  0
         throw new IllegalStateException(TapestryMessages.providedByEnhancement("getRenderWorker"));
 900  
     }
 901  
     
 902  
     /**
 903  
      * {@inheritDoc}
 904  
      */
 905  
     public boolean isStateful()
 906  
     {
 907  2
         return false;
 908  
     }
 909  
     
 910  
     /**
 911  
      * {@inheritDoc}
 912  
      */
 913  
     public int hashCode()
 914  
     {
 915  4
         final int prime = 31;
 916  4
         int result = 1;
 917  4
         result = prime * result + ((getClientId() == null) ? 0 : getClientId().hashCode());
 918  4
         result = prime * result + ((_id == null) ? 0 : _id.hashCode());
 919  4
         return result;
 920  
     }
 921  
     
 922  
     /**
 923  
      * {@inheritDoc}
 924  
      */
 925  
     public boolean equals(Object obj)
 926  
     {
 927  380
         if (this == obj) return true;
 928  6
         if (obj == null) return false;
 929  6
         if (getClass() != obj.getClass()) return false;
 930  0
         final AbstractComponent other = (AbstractComponent) obj;
 931  0
         if (getClientId() == null) {
 932  0
             if (other.getClientId() != null) return false;
 933  0
         } else if (!getClientId().equals(other.getClientId())) return false;
 934  0
         if (_id == null) {
 935  0
             if (other._id != null) return false;
 936  0
         } else if (!_id.equals(other._id)) return false;
 937  0
         return true;
 938  
     }
 939  
 }