Repository: wicket Updated Branches: refs/heads/WICKET-5265 9c60d34e3 -> 19af28e9c
WICKET-5677 change onadd to onReAdd and only call when oninitialize wouldn't be called Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/4703c1ae Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/4703c1ae Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/4703c1ae Branch: refs/heads/WICKET-5265 Commit: 4703c1ae1af91fdcca44e34edbeeb8bbaeb63b6c Parents: 70b7327 Author: Carl-Eric Menzel <cmen...@apache.org> Authored: Mon Aug 18 13:35:44 2014 +0200 Committer: Carl-Eric Menzel <cmen...@apache.org> Committed: Mon Aug 18 13:35:44 2014 +0200 ---------------------------------------------------------------------- .../main/java/org/apache/wicket/Component.java | 430 ++++++++++--------- .../java/org/apache/wicket/MarkupContainer.java | 48 +-- .../test/java/org/apache/wicket/OnAddTest.java | 160 ------- .../java/org/apache/wicket/OnReAddTest.java | 155 +++++++ 4 files changed, 375 insertions(+), 418 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/4703c1ae/wicket-core/src/main/java/org/apache/wicket/Component.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/Component.java b/wicket-core/src/main/java/org/apache/wicket/Component.java index 0eb62c3..e812caa 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Component.java +++ b/wicket-core/src/main/java/org/apache/wicket/Component.java @@ -98,7 +98,7 @@ import org.slf4j.LoggerFactory; /** * Component serves as the highest level abstract base class for all components. - * + * * <ul> * <li><b>Identity </b>- All Components must have a non-null id which is retrieved by calling * getId(). The id must be unique within the {@link MarkupContainer} that holds the Component, but @@ -207,7 +207,7 @@ import org.slf4j.LoggerFactory; * <li><b>Security </b>- All components are subject to an {@link IAuthorizationStrategy} which * controls instantiation, visibility and enabling. See {@link SimplePageAuthorizationStrategy} for * a simple implementation.</li> - * + * * @author Jonathan Locke * @author Chris Turner * @author Eelco Hillenius @@ -241,14 +241,14 @@ public abstract class Component * When a component is not allowed to be enabled (in effect disabled through the implementation * of this interface), Wicket will try to prevent model updates too. This is not completely fail * safe, as constructs like: - * + * * <pre> - * + * * User u = (User)getModelObject(); * u.setName("got you there!"); - * + * * </pre> - * + * * can't be prevented. Indeed it can be argued that any model protection is best dealt with in * your model objects to be completely secured. Wicket will catch all normal framework-directed * use though. @@ -447,7 +447,7 @@ public abstract class Component private static final short RFLAG_CONFIGURED = 0x10; private static final short RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED = 0x20; private static final short RFLAG_INITIALIZE_SUPER_CALL_VERIFIED = 0x40; - private static final short RFLAG_ONADD_SUPER_CALL_VERIFIED = 0x80; + private static final short RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED = 0x80; /** * Flags that only keep their value during the request. Useful for cache markers, etc. At the @@ -655,7 +655,7 @@ public abstract class Component /** * Constructor. All components have names. A component's id cannot be null. This is the minimal * constructor of component. It does not register a model. - * + * * @param id * The non-null id of this component * @throws WicketRuntimeException @@ -669,12 +669,12 @@ public abstract class Component /** * Constructor. All components have names. A component's id cannot be null. This constructor * includes a model. - * + * * @param id * The non-null id of this component * @param model * The component's model - * + * * @throws WicketRuntimeException * Thrown if the component has been given a null id. */ @@ -718,9 +718,9 @@ public abstract class Component * Panel/Border/Enclosure.getMarkup(null) to return the associated markup file. And * Panel/Border/Enclosure.getMarkup(child) will search the child in the appropriate markup * fragment. - * + * * @see MarkupContainer#getMarkup(Component) - * + * * @return The markup fragment */ public IMarkupFragment getMarkup() @@ -779,7 +779,7 @@ public abstract class Component * Set the markup for the component. Note that the component's markup variable is transient and * thus must only be used for one render cycle. E.g. auto-component are using it. You may also * it if you subclassed getMarkup(). - * + * * @param markup */ public final void setMarkup(final IMarkupFragment markup) @@ -829,16 +829,16 @@ public abstract class Component * <p> * Parent containers are guaranteed to be initialized before their children * </p> - * + * * <p> * It is safe to use {@link #getPage()} in this method * </p> - * + * * <p> * NOTE:The timing of this call is not precise, the contract is that it is called sometime * before {@link Component#onBeforeRender()}. * </p> - * + * */ protected void onInitialize() { @@ -847,7 +847,7 @@ public abstract class Component /** * Checks if the component has been initialized - {@link #onInitialize()} has been called - * + * * @return {@code true} if component has been initialized */ final boolean isInitialized() @@ -857,7 +857,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT - * + * * Used to call {@link #onInitialize()} */ public void internalInitialize() @@ -886,6 +886,18 @@ public abstract class Component getApplication().getComponentInitializationListeners().onInitialize(this); } + else + { + setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, false); + onReAdd(); + if (!getRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED)) + { + throw new IllegalStateException(Component.class.getName() + + " has not been properly added. Something in the hierarchy of " + + getClass().getName() + + " has not called super.onReAdd() in the override of onReAdd() method"); + } + } } /** @@ -920,7 +932,7 @@ public abstract class Component } /** - * + * */ private final void internalBeforeRender() { @@ -1015,14 +1027,14 @@ public abstract class Component * visibility or enabled or other state of one component depends on the state of another this * method should be manually invoked on the other component by the user. EG to link visiliby of * two markup containers the following should be done: - * + * * <pre> * final WebMarkupContainer source=new WebMarkupContainer("a") { * protected void onConfigure() { * setVisible(Math.rand()>0.5f); * } * }; - * + * * WebMarkupContainer linked=new WebMarkupContainer("b") { * protected void onConfigure() { * source.configure(); // make sure source is configured @@ -1030,7 +1042,7 @@ public abstract class Component * } * } * </pre> - * + * * </p> */ public final void configure() @@ -1060,9 +1072,9 @@ public abstract class Component } /** - * + * * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * Called after the {@link #onConfigure()}, but before {@link #onBeforeRender()} */ void internalOnAfterConfigure() @@ -1073,9 +1085,9 @@ public abstract class Component * Redirects to any intercept page previously specified by a call to redirectToInterceptPage. * The redirect is done by throwing an exception. If there is no intercept page no exception * will be thrown and the program flow will continue uninterrupted. - * + * * Example: - * + * * <pre> * add(new Link("login") * { @@ -1089,7 +1101,7 @@ public abstract class Component * } * } * }); - * + * * @see Component#redirectToInterceptPage(Page) */ public final void continueToOriginalDestination() @@ -1107,7 +1119,7 @@ public abstract class Component /** * Registers a debug feedback message for this component - * + * * @param message * The feedback message */ @@ -1230,7 +1242,7 @@ public abstract class Component /** * Registers an error feedback message for this component - * + * * @param message * The feedback message */ @@ -1242,7 +1254,7 @@ public abstract class Component /** * Registers a fatal feedback message for this component - * + * * @param message * The feedback message */ @@ -1254,11 +1266,11 @@ public abstract class Component /** * Finds the first container parent of this component of the given class. - * + * * @param <Z> * type of parent - * - * + * + * * @param c * MarkupContainer class to search for * @return First container parent that is an instance of the given class, or null if none can be @@ -1307,7 +1319,7 @@ public abstract class Component /** * Gets interface to application that this component is a part of. - * + * * @return The application associated with the session that this component is in. * @see Application */ @@ -1327,10 +1339,10 @@ public abstract class Component /** * Gets the converter that should be used by this component. - * + * * @param type * The type to convert to - * + * * @return The converter that should be used by this component */ @Override @@ -1341,7 +1353,7 @@ public abstract class Component /** * Gets whether model strings should be escaped. - * + * * @return Returns whether model strings should be escaped */ public final boolean getEscapeModelStrings() @@ -1351,7 +1363,7 @@ public abstract class Component /** * Gets the id of this component. - * + * * @return The id of this component */ @Override @@ -1371,7 +1383,7 @@ public abstract class Component /** * Gets the locale for this component. By default, it searches its parents for a locale. If no * parents (it's a recursive search) returns a locale, it gets one from the session. - * + * * @return The locale to be used for this component * @see Session#getLocale() */ @@ -1386,7 +1398,7 @@ public abstract class Component /** * Convenience method to provide easy access to the localizer object within any component. - * + * * @return The localizer object */ public final Localizer getLocalizer() @@ -1396,7 +1408,7 @@ public abstract class Component /** * Get the first component tag in the associated markup - * + * * @return first component tag */ private final ComponentTag getMarkupTag() @@ -1418,14 +1430,14 @@ public abstract class Component /** * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT. - * + * * Get a copy of the markup's attributes which are associated with the component. * <p> * Modifications to the map returned don't change the tags attributes. It is just a copy. * <p> * Note: The component must have been added (directly or indirectly) to a container with an * associated markup file (Page, Panel or Border). - * + * * @return markup attributes */ public final ValueMap getMarkupAttributes() @@ -1442,7 +1454,7 @@ public abstract class Component /** * Get the markupId - * + * * @return MarkupId */ public final Object getMarkupIdImpl() @@ -1473,15 +1485,15 @@ public abstract class Component * the preferred way as there is no chance of id collision. This will also enable * {@link #setOutputMarkupId(boolean)}. * <p> - * + * * <p> * Note: This method should only be called after the component or its parent have been added to * the page. - * + * * @param createIfDoesNotExist * When there is no existing markup id, determines whether it should be generated or * whether <code>null</code> should be returned. - * + * * @return markup id of the component */ public String getMarkupId(boolean createIfDoesNotExist) @@ -1502,7 +1514,7 @@ public abstract class Component * <p> * Note: This method should only be called after the component or its parent have been added to * the page. - * + * * @return markup id of the component */ public String getMarkupId() @@ -1512,10 +1524,10 @@ public abstract class Component /** * Gets metadata for this component using the given key. - * + * * @param <M> * The type of the metadata. - * + * * @param key * The key for the data * @return The metadata or null of no metadata was found for the given key @@ -1527,7 +1539,7 @@ public abstract class Component } /** - * + * * @return meta data entry */ private MetaDataEntry<?>[] getMetaData() @@ -1557,7 +1569,7 @@ public abstract class Component /** * Gets the model. It returns the object that wraps the backing model. - * + * * @return The model */ public final IModel<?> getDefaultModel() @@ -1577,7 +1589,7 @@ public abstract class Component /** * Gets the backing model object. Unlike getDefaultModel().getObject(), this method returns null * for a null model. - * + * * @return The backing model object */ public final Object getDefaultModelObject() @@ -1608,10 +1620,10 @@ public abstract class Component * sensitive chars are escaped but not all none-ascii chars. Proper HTML encoding should be used * instead. In case you really need a fully escaped model string you may call * {@link Strings#escapeMarkup(CharSequence, boolean, boolean)} on the model string returned. - * + * * @see Strings#escapeMarkup(CharSequence, boolean, boolean) * @see #getEscapeModelStrings() - * + * * @return Model object for this component as a string */ public final String getDefaultModelObjectAsString() @@ -1625,10 +1637,10 @@ public abstract class Component * sensitive chars are escaped but not all none-ascii chars. Proper HTML encoding should be used * instead. In case you really need a fully escaped model string you may call * {@link Strings#escapeMarkup(CharSequence, boolean, boolean)} on the model string returned. - * + * * @see Strings#escapeMarkup(CharSequence, boolean, boolean) * @see #getEscapeModelStrings() - * + * * @param modelObject * Model object to convert to string * @return The string @@ -1663,7 +1675,7 @@ public abstract class Component /** * Gets whether or not component will output id attribute into the markup. id attribute will be * set to the value returned from {@link Component#getMarkupId()}. - * + * * @return whether or not component will output id attribute into the markup */ public final boolean getOutputMarkupId() @@ -1673,7 +1685,7 @@ public abstract class Component /** * Gets whether or not an invisible component will render a placeholder tag. - * + * * @return true if a placeholder tag should be rendered */ public final boolean getOutputMarkupPlaceholderTag() @@ -1683,7 +1695,7 @@ public abstract class Component /** * Gets the page holding this component. - * + * * @return The page holding this component * @throws IllegalStateException * Thrown if component is not yet attached to a Page. @@ -1707,7 +1719,7 @@ public abstract class Component /** * Gets the path to this component relative to its containing page, i.e. without leading page * id. - * + * * @return The path to this component relative to the page it is in */ @Override @@ -1718,7 +1730,7 @@ public abstract class Component /** * Gets any parent container, or null if there is none. - * + * * @return Any parent container, or null if there is none */ @Override @@ -1729,7 +1741,7 @@ public abstract class Component /** * Gets this component's path. - * + * * @return Colon separated path to this component in the component hierarchy */ public final String getPath() @@ -1749,7 +1761,7 @@ public abstract class Component /** * If false the component's tag will be printed as well as its body (which is default). If true * only the body will be printed, but not the component's tag. - * + * * @return If true, the component tag will not be printed */ public final boolean getRenderBodyOnly() @@ -1774,7 +1786,7 @@ public abstract class Component /** * Gets the active request cycle for this component - * + * * @return The request cycle */ public final RequestCycle getRequestCycle() @@ -1792,7 +1804,7 @@ public abstract class Component /** * Gets the current Session object. - * + * * @return The Session that this component is in */ public Session getSession() @@ -1861,9 +1873,9 @@ public abstract class Component /** * A convenience method to access the Sessions's style. - * + * * @return The style of this component respectively the style of the Session. - * + * * @see org.apache.wicket.Session#getStyle() */ public final String getStyle() @@ -1880,7 +1892,7 @@ public abstract class Component * Gets the variation string of this component that will be used to look up markup for this * component. Subclasses can override this method to define by an instance what markup variation * should be picked up. By default it will return null or the value of a parent. - * + * * @return The variation of this component. */ public String getVariation() @@ -1894,7 +1906,7 @@ public abstract class Component /** * Gets whether this component was rendered at least once. - * + * * @return true if the component has been rendered before, false if it is merely constructed */ public final boolean hasBeenRendered() @@ -1907,7 +1919,7 @@ public abstract class Component * {@link FeedbackMessages} instance and add it to the component metadata, even when called on a * component that has no feedback messages, to avoid the overhead use * {@link #hasFeedbackMessage()} - * + * * @return feedback messages instance */ public FeedbackMessages getFeedbackMessages() @@ -1936,7 +1948,7 @@ public abstract class Component /** * @return True if this component has some kind of feedback message - * + * */ public final boolean hasFeedbackMessage() { @@ -1950,7 +1962,7 @@ public abstract class Component /** * Registers an informational feedback message for this component - * + * * @param message * The feedback message */ @@ -1962,7 +1974,7 @@ public abstract class Component /** * Registers an success feedback message for this component - * + * * @param message * The feedback message */ @@ -1974,7 +1986,7 @@ public abstract class Component /** * Authorizes an action for a component. - * + * * @param action * The action to authorize * @return True if the action is allowed @@ -2003,7 +2015,7 @@ public abstract class Component * Gets whether this component is enabled. Specific components may decide to implement special * behavior that uses this property, like web form components that add a disabled='disabled' * attribute when enabled is false. - * + * * @return Whether this component is enabled. */ public boolean isEnabled() @@ -2014,7 +2026,7 @@ public abstract class Component /** * Checks the security strategy if the {@link Component#RENDER} action is allowed on this * component - * + * * @return ture if {@link Component#RENDER} action is allowed, false otherwise */ public final boolean isRenderAllowed() @@ -2026,7 +2038,7 @@ public abstract class Component * Returns if the component is stateless or not. It checks the stateless hint if that is false * it returns directly false. If that is still true it checks all its behaviors if they can be * stateless. - * + * * @return whether the component is stateless. */ public final boolean isStateless() @@ -2092,7 +2104,7 @@ public abstract class Component * method, it is a good idea to keep it cheap in terms of processing. Alternatively, you can * call {@link #setVisible(boolean)}. * <p> - * + * * @return True if component and any children are visible */ public boolean isVisible() @@ -2102,7 +2114,7 @@ public abstract class Component /** * Checks if the component itself and all its parents are visible. - * + * * @return true if the component and all its parents are visible. */ public final boolean isVisibleInHierarchy() @@ -2120,14 +2132,14 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * Sets the RENDERING flag and removes the PREPARED_FOR_RENDER flag on component and it's * children. - * + * * @param setRenderingFlag * if this is false only the PREPARED_FOR_RENDER flag is removed from component, the * RENDERING flag is not set. - * + * * @see #internalPrepareForRender(boolean) */ public final void markRendering(boolean setRenderingFlag) @@ -2168,7 +2180,7 @@ public abstract class Component * <p> * Prepares the component and it's children for rendering. On whole page render this method must * be called on the page. On AJAX request, this method must be called on the updated component. - * + * * @param setRenderingFlag * Whether to set the rendering flag. This must be true if the page is about to be * rendered. However, there are usecases to call this method without an immediate @@ -2210,7 +2222,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * Prepares the component and it's children for rendering. On whole page render this method must * be called on the page. On AJAX request, this method must be called on updated component. */ @@ -2224,10 +2236,10 @@ public abstract class Component * is saved for future use by method continueToOriginalDestination(); Only use this method when * you plan to continue to the current url at some later time; otherwise just use * setResponsePage or - when you are in a constructor or checkAccessMethod, call redirectTo. - * + * * @param page * The sign in page - * + * * @see Component#continueToOriginalDestination() */ public final void redirectToInterceptPage(final Page page) @@ -2370,7 +2382,7 @@ public abstract class Component /** * Called when a runtime exception is caught during the render process - * + * * @param ex * The exception caught. */ @@ -2400,7 +2412,7 @@ public abstract class Component /** * Renders a placeholder tag for the component when it is invisible and * {@link #setOutputMarkupPlaceholderTag(boolean)} has been called with <code>true</code>. - * + * * @param tag * component tag * @param response @@ -2432,7 +2444,7 @@ public abstract class Component * Returns the id of the markup region that will be updated via ajax. This can be different to * the markup id of the component if a {@link IAjaxRegionMarkupIdProvider} behavior has been * added. - * + * * @return the markup id of the region to be updated via ajax. */ public final String getAjaxRegionMarkupId() @@ -2541,7 +2553,7 @@ public abstract class Component } /** - * + * * @param openTag * @return true, if the tag shall be rendered */ @@ -2582,7 +2594,7 @@ public abstract class Component /** * Get the markup sourcing strategy for the component. If null, * {@link #newMarkupSourcingStrategy()} will be called. - * + * * @return Markup sourcing strategy */ protected final IMarkupSourcingStrategy getMarkupSourcingStrategy() @@ -2608,7 +2620,7 @@ public abstract class Component * Please note that markup source strategies are not persisted. Instead they get re-created by * calling this method again. That's ok since markup sourcing strategies usually do not maintain * a state. - * + * * @return Markup sourcing strategy */ protected IMarkupSourcingStrategy newMarkupSourcingStrategy() @@ -2618,14 +2630,14 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT. - * + * * Print to the web response what ever the component wants to contribute to the head section. * Make sure that all attached behaviors are asked as well. * <p> * NOT intended for overriding by framework clients. Rather, use * {@link Component#renderHead(org.apache.wicket.markup.head.IHeaderResponse)} * </p> - * + * * @param container * The HtmlHeaderContainer */ @@ -2686,16 +2698,16 @@ public abstract class Component /** * Replaces this component with another. The replacing component must have the same component id * as this component. This method serves as a shortcut to - * + * * <code>this.getParent().replace(replacement)</code> - * + * * and provides a better context for errors. * <p> * Usage: <code>component = component.replaceWith(replacement);</code> * </p> - * + * * @since 1.2.1 - * + * * @param replacement * component to replace this one * @return the component which replaced this one @@ -2754,7 +2766,7 @@ public abstract class Component * attribute when enabled is false. If it is not enabled, it will not be allowed to call any * listener method on it (e.g. Link.onClick) and the model object will be protected (for the * common use cases, not for programmer's misuse) - * + * * @param enabled * whether this component is enabled * @return This @@ -2793,7 +2805,7 @@ public abstract class Component /** * Sets whether model strings should be escaped. - * + * * @param escapeMarkup * True is model strings should be escaped * @return This @@ -2806,7 +2818,7 @@ public abstract class Component /** * Set markup ID, which must be String or Integer - * + * * @param markupId */ public final void setMarkupIdImpl(Object markupId) @@ -2831,7 +2843,7 @@ public abstract class Component /** * Copy markupId - * + * * @param comp */ final void setMarkupId(Component comp) @@ -2855,9 +2867,9 @@ public abstract class Component * <p> * If null is passed in the user defined value is cleared and markup id value will fall back on * automatically generated value - * + * * @see #getMarkupId() - * + * * @param markupId * markup id value or null to clear any previous user defined value * @return this for chaining @@ -2878,10 +2890,10 @@ public abstract class Component * Sets the metadata for this component using the given key. If the metadata object is not of * the correct type for the metadata key, an IllegalArgumentException will be thrown. For * information on creating MetaDataKeys, see {@link MetaDataKey}. - * + * * @param <M> * The type of the metadata - * + * * @param key * The singleton key for the metadata * @param object @@ -2922,7 +2934,7 @@ public abstract class Component * WARNING: DO NOT OVERRIDE THIS METHOD UNLESS YOU HAVE A VERY GOOD REASON FOR IT. OVERRIDING * THIS MIGHT OPEN UP SECURITY LEAKS AND BREAK BACK-BUTTON SUPPORT. * </p> - * + * * @param model * The model * @return This @@ -2970,7 +2982,7 @@ public abstract class Component } /** - * + * * @param model */ void setModelImpl(IModel<?> model) @@ -3001,7 +3013,7 @@ public abstract class Component * Sets the backing model object. Unlike <code>getDefaultModel().setObject(object)</code>, this * method checks authorisation and model comparator, and invokes <code>modelChanging</code> and * <code>modelChanged</code> if the value really changes. - * + * * @param object * The object to set * @return This @@ -3038,13 +3050,13 @@ public abstract class Component /** * Sets whether or not component will output id attribute into the markup. id attribute will be * set to the value returned from {@link Component#getMarkupId()}. - * + * * @param output * True if the component will output the id attribute into markup. Please note that * the default behavior is to use the same id as the component. This means that your * component must begin with [a-zA-Z] in order to generate a valid markup id * according to: http://www.w3.org/TR/html401/types.html#type-name - * + * * @return this for chaining */ public final Component setOutputMarkupId(final boolean output) @@ -3057,15 +3069,15 @@ public abstract class Component * Render a placeholder tag when the component is not visible. The tag is of form: * <componenttag style="display:none;" id="markupid"/>. This method will also call * <code>setOutputMarkupId(true)</code>. - * + * * This is useful, for example, in ajax situations where the component starts out invisible and * then becomes visible through an ajax update. With a placeholder tag already in the markup you * do not need to repaint this component's parent, instead you can repaint the component * directly. - * + * * When this method is called with parameter <code>false</code> the outputmarkupid flag is not * reverted to false. - * + * * @param outputTag * @return this for chaining */ @@ -3091,7 +3103,7 @@ public abstract class Component /** * If false the component's tag will be printed as well as its body (which is default). If true * only the body will be printed, but not the component's tag. - * + * * @param renderTag * If true, the component tag will not be printed * @return This @@ -3104,9 +3116,9 @@ public abstract class Component /** * Sets the page that will respond to this request - * + * * @param <C> - * + * * @param cls * The response page class * @see RequestCycle#setResponsePage(Class) @@ -3118,9 +3130,9 @@ public abstract class Component /** * Sets the page class and its parameters that will respond to this request - * + * * @param <C> - * + * * @param cls * The response page class * @param parameters @@ -3135,10 +3147,10 @@ public abstract class Component /** * Sets the page that will respond to this request - * + * * @param page * The response page - * + * * @see RequestCycle#setResponsePage(org.apache.wicket.request.component.IRequestablePage) */ public final void setResponsePage(final Page page) @@ -3160,7 +3172,7 @@ public abstract class Component /** * Sets whether this component and any children are visible. - * + * * @param visible * True if this component and any children should be visible * @return This @@ -3193,7 +3205,7 @@ public abstract class Component /** * Gets the string representation of this component. - * + * * @return The path to this component */ @Override @@ -3261,11 +3273,11 @@ public abstract class Component * Returns a bookmarkable URL that references a given page class using a given set of page * parameters. Since the URL which is returned contains all information necessary to instantiate * and render the page, it can be stored in a user's browser as a stable bookmark. - * + * * @param <C> - * + * * @see RequestCycle#urlFor(Class, org.apache.wicket.request.mapper.parameter.PageParameters) - * + * * @param pageClass * Class of page * @param parameters @@ -3281,7 +3293,7 @@ public abstract class Component /** * Gets a URL for the listener interface on a behavior (e.g. IBehaviorListener on * AjaxPagingNavigationBehavior). - * + * * @param behaviour * The behavior that the URL should point to * @param listener @@ -3322,12 +3334,12 @@ public abstract class Component /** * Returns a URL that references the given request target. - * + * * @see RequestCycle#urlFor(IRequestHandler) - * + * * @param requestHandler * the request target to reference - * + * * @return a URL that references the given request target */ public final CharSequence urlFor(final IRequestHandler requestHandler) @@ -3337,9 +3349,9 @@ public abstract class Component /** * Gets a URL for the listener interface (e.g. ILinkListener). - * + * * @see RequestCycle#urlFor(IRequestHandler) - * + * * @param listener * The listener interface that the URL should call * @param parameters @@ -3355,9 +3367,9 @@ public abstract class Component /** * Returns a URL that references a shared resource through the provided resource reference. - * + * * @see RequestCycle#urlFor(IRequestHandler) - * + * * @param resourceReference * The resource reference * @param parameters @@ -3373,7 +3385,7 @@ public abstract class Component /** * Traverses all parent components of the given class in this parentClass, calling the visitor's * visit method at each one. - * + * * @param <R> * the type of the result object * @param parentClass @@ -3391,7 +3403,7 @@ public abstract class Component /** * Traverses all parent components of the given class in this parentClass, calling the visitor's * visit method at each one. - * + * * @param <R> * the type of the result object * @param parentClass @@ -3435,7 +3447,7 @@ public abstract class Component /** * Registers a warning feedback message for this component. - * + * * @param message * The feedback message */ @@ -3479,7 +3491,7 @@ public abstract class Component /** * TODO WICKET-NG rename to something more useful - like componentChanged(), this method used to * be called with a Change object - * + * * Adds state change to page. */ protected final void addStateChange() @@ -3494,7 +3506,7 @@ public abstract class Component /** * Checks whether the given type has the expected name. - * + * * @param tag * The tag to check * @param name @@ -3516,7 +3528,7 @@ public abstract class Component /** * Checks that a given tag has a required attribute value. - * + * * @param tag * The tag * @param key @@ -3546,7 +3558,7 @@ public abstract class Component /** * Checks whether the hierarchy may be changed at all, and throws an exception if this is not * the case. - * + * * @param component * the component which is about to be added or removed */ @@ -3580,7 +3592,7 @@ public abstract class Component /** * Suffixes an exception message with useful information about this. component. - * + * * @param message * The message * @return The modified message @@ -3592,7 +3604,7 @@ public abstract class Component /** * Finds the markup stream for this component. - * + * * @return The markup stream for this component. Since a Component cannot have a markup stream, * we ask this component's parent to search for it. */ @@ -3604,7 +3616,7 @@ public abstract class Component /** * If this Component is a Page, returns self. Otherwise, searches for the nearest Page parent in * the component hierarchy. If no Page parent can be found, null is returned. - * + * * @return The Page or null if none can be found */ protected final Page findPage() @@ -3617,7 +3629,7 @@ public abstract class Component * Gets the subset of the currently coupled {@link Behavior}s that are of the provided type as * an unmodifiable list. Returns an empty list if there are no behaviors coupled to this * component. - * + * * @param type * The type or null for all * @return The subset of the currently coupled behaviors that are of the provided type as an @@ -3632,7 +3644,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * @param flag * The flag to test * @return True if the flag is set @@ -3644,7 +3656,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * @param flag * The flag to test * @return True if the flag is set @@ -3656,7 +3668,7 @@ public abstract class Component /** * Finds the innermost IModel object for an IModel that might contain nested IModel(s). - * + * * @param model * The model * @return The innermost (most nested) model @@ -3679,7 +3691,7 @@ public abstract class Component /** * Gets the component's current model comparator. Implementations can be used for testing the * current value of the components model data with the new value that is given. - * + * * @return the value defaultModelComparator */ public IModelComparator getModelComparator() @@ -3692,7 +3704,7 @@ public abstract class Component * stateless, otherwise the component will be treat as stateful. In order for page to be * stateless (and not to be stored in session), all components (and component behaviors) must be * stateless. - * + * * @return whether the component can be stateless */ protected boolean getStatelessHint() @@ -3711,7 +3723,7 @@ public abstract class Component * For example a {@link FormComponent} has the opportunity to instantiate a model on the fly * using its {@code id} and the containing {@link Form}'s model, if the form holds a * {@link CompoundPropertyModel}. - * + * * @return The model */ protected IModel<?> initModel() @@ -3746,7 +3758,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL OR OVERRIDE. - * + * * <p> * Called anytime a model is changed via setModel or setModelObject. * </p> @@ -3757,7 +3769,7 @@ public abstract class Component /** * Components are allowed to reject behavior modifiers. - * + * * @param behavior * @return False, if the component should not apply this behavior */ @@ -3775,7 +3787,7 @@ public abstract class Component /** * If true, all attribute modifiers will be ignored - * + * * @return True, if attribute modifiers are to be ignored */ protected final boolean isIgnoreAttributeModifier() @@ -3796,7 +3808,7 @@ public abstract class Component * <p> * <strong>NOTE</strong>: If you override this, you *must* call super.onBeforeRender() within * your implementation. - * + * * Because this method is responsible for cascading {@link #onBeforeRender()} call to its * children it is strongly recommended that super call is made at the end of the override. * </p> @@ -3804,7 +3816,7 @@ public abstract class Component * Changes to the component tree can be made only <strong>before</strong> calling * super.onBeforeRender(). * - * @see org.apache.wicket.MarkupContainer#addOrReplace(Component...) + * @see org.apache.wicket.MarkupContainer#addOrReplace(Component...) */ protected void onBeforeRender() { @@ -3815,9 +3827,9 @@ public abstract class Component /** * Processes the component tag. - * + * * Overrides of this method most likely should call the super implementation. - * + * * @param tag * Tag to modify */ @@ -3846,7 +3858,7 @@ public abstract class Component /** * Processes the body. - * + * * @param markupStream * The markup stream * @param openTag @@ -3858,7 +3870,7 @@ public abstract class Component /** * Called to allow a component to detach resources after use. - * + * * Overrides of this method MUST call the super implementation, the most logical place to do * this is the last line of the override method. */ @@ -3869,7 +3881,7 @@ public abstract class Component /** * Called to notify the component it is being removed from the component hierarchy - * + * * Overrides of this method MUST call the super implementation, the most logical place to do * this is the last line of the override method. */ @@ -3901,7 +3913,7 @@ public abstract class Component * Writes a simple tag out to the response stream. Any components that might be referenced by * the tag are ignored. Also undertakes any tag attribute modifications if they have been added * to the component. - * + * * @param tag * The tag to write */ @@ -3966,7 +3978,7 @@ public abstract class Component /** * Replaces the body with the given one. - * + * * @param markupStream * The markup stream to replace the tag body in * @param tag @@ -4029,7 +4041,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * @param flag * The flag to set * @param set @@ -4049,7 +4061,7 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * @param flag * The flag to set * @param set @@ -4069,7 +4081,7 @@ public abstract class Component /** * If true, all attribute modifiers will be ignored - * + * * @param ignore * If true, all attribute modifiers will be ignored * @return This @@ -4112,7 +4124,7 @@ public abstract class Component /** * Gets the component at the given path. - * + * * @param path * Path to component * @return The component at the path @@ -4159,7 +4171,7 @@ public abstract class Component } /** - * + * * @return <code>true</code> if component has been prepared for render */ boolean isPreparedForRender() @@ -4168,7 +4180,7 @@ public abstract class Component } /** - * + * */ protected void onAfterRenderChildren() { @@ -4186,7 +4198,7 @@ public abstract class Component /** * Renders the close tag at the current position in the markup stream. - * + * * @param markupStream * the markup stream * @param openTag @@ -4220,9 +4232,9 @@ public abstract class Component /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! - * + * * Sets the id of this component. - * + * * @param id * The non-null id of this component */ @@ -4246,13 +4258,13 @@ public abstract class Component /** * THIS IS A WICKET INTERNAL API. DO NOT USE IT. - * + * * Sets the parent of a component. Typically what you really want is parent.add(child). * <p/> * Note that calling setParent() and not parent.add() will connect the child to the parent, but * the parent will not know the child. This might not be a problem in some cases, but e.g. * child.onDetach() will not be invoked (since the parent doesn't know it is his child). - * + * * @param parent * The parent container */ @@ -4267,7 +4279,7 @@ public abstract class Component /** * Sets the render allowed flag. - * + * * @param renderAllowed */ final void setRenderAllowed(boolean renderAllowed) @@ -4277,7 +4289,7 @@ public abstract class Component /** * Sets the render allowed flag. - * + * * Visit all this page's children (overridden in MarkupContainer) to check rendering * authorization, as appropriate. We set any result; positive or negative as a temporary boolean * in the components, and when a authorization exception is thrown it will block the rendering @@ -4294,7 +4306,7 @@ public abstract class Component * {@link #setVisible(boolean)} will not always have a desired effect because that component may * have {@link #isVisible()} overridden. Both {@link #setVisibilityAllowed(boolean)} and * {@link #isVisibilityAllowed()} are <code>final</code> so their contract is enforced always. - * + * * @param allowed * @return <code>this</code> for chaining */ @@ -4307,7 +4319,7 @@ public abstract class Component /** * Gets whether or not visibility is allowed on this component. See * {@link #setVisibilityAllowed(boolean)} for details. - * + * * @return true if this component is allowed to be visible, false otherwise. */ public final boolean isVisibilityAllowed() @@ -4318,7 +4330,7 @@ public abstract class Component /** * Determines whether or not a component should be visible, taking into account all the factors: * {@link #isVisible()}, {@link #isVisibilityAllowed()}, {@link #isRenderAllowed()} - * + * * @return <code>true</code> if the component should be visible, <code>false</code> otherwise */ public final boolean determineVisibility() @@ -4331,7 +4343,7 @@ public abstract class Component * Calculates enabled state of the component taking its hierarchy into account. A component is * enabled iff it is itself enabled ({@link #isEnabled()} and {@link #isEnableAllowed()} both * return <code>true</code>), and all of its parents are enabled. - * + * * @return <code>true</code> if this component is enabled</code> */ public final boolean isEnabledInHierarchy() @@ -4374,11 +4386,11 @@ public abstract class Component * method so that image data can be streamed. Such a component would override this method and * return {@literal true} if the listener method belongs to {@link IRequestListener}. * </p> - * + * * @param method * listener method about to be invoked on this component. Could be {@code null} - in * this case it means <em>any</em> method. - * + * * @return {@literal true} iff the listener method can be invoked on this component */ public boolean canCallListenerInterface(Method method) @@ -4391,7 +4403,7 @@ public abstract class Component * implementing {@link IHeaderContributor}. overload * {@link Component#renderHead(org.apache.wicket.markup.head.IHeaderResponse)} instead to * contribute to the response header. - * + * * @param component * @param response */ @@ -4407,7 +4419,7 @@ public abstract class Component /** * Render to the web response whatever the component wants to contribute to the head section. - * + * * @param response * Response object */ @@ -4433,10 +4445,10 @@ public abstract class Component /** * Removes behavior from component - * + * * @param behaviors * behaviors to remove - * + * * @return this (to allow method call chaining) */ public Component remove(final Behavior... behaviors) @@ -4465,7 +4477,7 @@ public abstract class Component /** * Adds a behavior modifier to the component. - * + * * @param behaviors * The behavior modifier(s) to be added * @return this (to allow method call chaining) @@ -4479,7 +4491,7 @@ public abstract class Component /** * Gets the currently coupled {@link Behavior}s as a unmodifiable list. Returns an empty list * rather than null if there are no behaviors coupled to this component. - * + * * @return The currently coupled behaviors as a unmodifiable list */ public final List<? extends Behavior> getBehaviors() @@ -4487,32 +4499,28 @@ public abstract class Component return getBehaviors(null); } - final void internalOnAdd() - { - setRequestFlag(RFLAG_ONADD_SUPER_CALL_VERIFIED, false); - onAddToPage(); - if (!getRequestFlag(RFLAG_ONADD_SUPER_CALL_VERIFIED)) - { - throw new IllegalStateException(Component.class.getName() + - " has not been properly added. Something in the hierarchy of " + - getClass().getName() + - " has not called super.onAddToPage() in the override of onAddToPage() method"); - } - } - /** - * This method is called whenever a component is added to the component tree connected to the page, - * to allow it to initialize things that depend on other components in the page. This is similar - * to {@link #onInitialize()}. However, onInitialize is only ever called once, the first time the - * component is added. It is not called when a component is removed and then added again. + * This method is called whenever a component is re-added to the page's component tree, if it + * had been removed at some earlier time, i.e., if it is already initialized + * (see {@link org.apache.wicket.Component#isInitialized()}). + * + * This is similar to onInitialize, but only comes after the component has been removed and + * then + * added again: + * + * <ul><li>onInitialize is only called the very first time a component is added</li> + * <li>onReAdd is not called the first time, but every time it is re-added after having been + * removed</li> + * </ul> * - * This method, however, is called every time the component is added to the page's tree. It is - * never called before onInitialize(). It is thus the opposite of onRemove(). + * You can think of it as the opposite of onRemove. A component that was once removed will + * not be + * re-initialized but only re-added. * - * Subclasses that override this must call super.onAddToPage(). + * Subclasses that override this must call super.onReAdd(). */ - protected void onAddToPage() + protected void onReAdd() { - setRequestFlag(RFLAG_ONADD_SUPER_CALL_VERIFIED, true); + setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, true); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/4703c1ae/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java index cbb1d05..66ebf17 100644 --- a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java +++ b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java @@ -939,17 +939,6 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp } } - // onAdd should only be triggered - // - after onInitialize - // - if we are not initialized ourselves yet, then we delay calling onAdd until onInitialize - // - if we are initialized already, we can just call onAdd now - // - AND if we already have a page. See #onAdd - // - if we don't have a page yet, our own onAdd will be called when we get added to the page component tree. - if (page != null && this.isInitialized()) - { - child.internalOnAdd(); - } - // if the PREPARED_FOR_RENDER flag is set, we have already called // beforeRender on this component's children. So we need to initialize the newly added one if (isPreparedForRender()) @@ -958,36 +947,7 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp } } - @Override protected void onAddToPage() - { - super.onAddToPage(); - Component[] children = copyChildren(); - try - { - for (final Component child : children) - { - // We need to check whether the child's wasn't removed from the - // component in the meanwhile (e.g. from another's child - // onAddToPage) - if (child.getParent() == this) - { - child.internalOnAdd(); - } - } - } - catch (RuntimeException ex) - { - if (ex instanceof WicketRuntimeException) - { - throw ex; - } - else - { - throw new WicketRuntimeException("Error adding this container: " + - this, ex); - } - } - } + /** * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT @@ -1988,10 +1948,4 @@ public abstract class MarkupContainer extends Component implements Iterable<Comp } } } - - @Override protected void onInitialize() - { - super.onInitialize(); - internalOnAdd(); - } } http://git-wip-us.apache.org/repos/asf/wicket/blob/4703c1ae/wicket-core/src/test/java/org/apache/wicket/OnAddTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/OnAddTest.java b/wicket-core/src/test/java/org/apache/wicket/OnAddTest.java deleted file mode 100644 index 117b22d..0000000 --- a/wicket-core/src/test/java/org/apache/wicket/OnAddTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.WebPage; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.util.tester.WicketTesterScope; -import org.junit.Rule; -import org.junit.Test; - -public class OnAddTest -{ - @Rule - public WicketTesterScope scope = new WicketTesterScope(); - - private boolean onAddCalled = false; - - private Component createProbe() - { - return new Label("foo") - { - @Override - protected void onAddToPage() - { - super.onAddToPage(); - onAddCalled = true; - } - }; - } - - @Test - public void onAddIsCalledIfParentIsInitialized() - { - Page page = createPage(); - page.internalInitialize(); - page.add(createProbe()); - assertTrue(onAddCalled); - } - - private WebPage createPage() - { - return new WebPage() - { - }; - } - - @Test - public void onAddIsNotCalledIfParentIsNotInitialized() - { - Page page = createPage(); - page.add(createProbe()); - assertFalse(onAddCalled); - } - - @Test - public void onAddIsCalledWhenParentIsInitialized() - { - Page page = createPage(); - page.add(createProbe()); - page.internalInitialize(); - assertTrue(onAddCalled); - } - - @Test - public void onAddIsNotCalledWhenParentIsNotConnectedToPage() - { - MarkupContainer container = createContainer(); - container.internalInitialize(); - container.add(createProbe()); - assertFalse(onAddCalled); - } - - @Test - public void onAddIsCalledWhenParentIsAddedToPage() - { - MarkupContainer container = createContainer(); - container.internalInitialize(); - container.add(createProbe()); - assertFalse(onAddCalled); - WebPage page = createPage(); - page.internalInitialize(); - page.add(container); - assertTrue(onAddCalled); - } - - @Test - public void onAddIsCalledAfterRemoveAndAdd() - { - Page page = createPage(); - page.internalInitialize(); - Component probe = createProbe(); - page.add(probe); - assertTrue(onAddCalled); - onAddCalled = false; - page.remove(probe); - assertFalse(onAddCalled); - page.add(probe); - assertTrue(onAddCalled); - } - - @Test - public void onAddRecursesToChildren() - { - Page page = createPage(); - page.internalInitialize(); - page.add(createNestedProbe()); - assertTrue(onAddCalled); - } - - @Test - public void onAddEnforcesSuperCall() - { - Page page = createPage(); - page.internalInitialize(); - try - { - page.add(new Label("foo") - { - @Override - protected void onAddToPage() - { - ; // I should call super, but since I don't, this should throw an exception - } - }); - fail("should have thrown exception"); - } catch (IllegalStateException e) - { - assertTrue(e.getMessage().contains("super.onAddToPage")); - } - } - - private Component createNestedProbe() - { - return createContainer().add(createProbe()); - } - - private MarkupContainer createContainer() - { - return new WebMarkupContainer("bar"); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/4703c1ae/wicket-core/src/test/java/org/apache/wicket/OnReAddTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/OnReAddTest.java b/wicket-core/src/test/java/org/apache/wicket/OnReAddTest.java new file mode 100644 index 0000000..989b163 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/OnReAddTest.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.wicket; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.util.tester.WicketTesterScope; +import org.junit.Rule; +import org.junit.Test; + +public class OnReAddTest +{ + @Rule + public WicketTesterScope scope = new WicketTesterScope(); + + private boolean onReAddCalled = false; + + private Component createUninitializedProbe() + { + return new Label("foo") + { + @Override + protected void onReAdd() + { + super.onReAdd(); + onReAddCalled = true; + } + }; + } + + private Component createInitializedProbe() { + Component probe = createUninitializedProbe(); + probe.internalInitialize(); + return probe; + } + + @Test + public void onAddIsNotCalledOnFirstAdd() + { + Page page = createPage(); + page.internalInitialize(); + page.add(createUninitializedProbe()); + assertFalse(onReAddCalled); + } + + private WebPage createPage() + { + return new WebPage() + { + }; + } + + @Test + public void onAddIsNotCalledWhenParentIsNotConnectedToPage() + { + MarkupContainer container = createContainer(); + container.internalInitialize(); + container.add(createUninitializedProbe()); + assertFalse(onReAddCalled); + } + + @Test + public void onAddIsNotCalledOnUninitializedProbeWhenParentIsAddedToPage() + { + MarkupContainer container = createContainer(); + container.internalInitialize(); + container.add(createUninitializedProbe()); + assertFalse(onReAddCalled); + WebPage page = createPage(); + page.internalInitialize(); + page.add(container); + assertFalse(onReAddCalled); + } + + @Test + public void onAddIsCalledAfterRemoveAndAdd() + { + Page page = createPage(); + page.internalInitialize(); + Component probe = createUninitializedProbe(); + page.add(probe); + assertFalse(onReAddCalled); + page.remove(probe); + assertFalse(onReAddCalled); + page.add(probe); + assertTrue(onReAddCalled); + } + + @Test + public void onAddRecursesToChildren() + { + Page page = createPage(); + page.internalInitialize(); + Component probe = createNestedProbe(); + page.add(probe); + assertFalse(onReAddCalled); + probe.remove(); + assertFalse(onReAddCalled); + page.add(probe); + assertTrue(onReAddCalled); + } + + @Test + public void onAddEnforcesSuperCall() + { + Page page = createPage(); + page.internalInitialize(); + try + { + Label brokenProbe = new Label("foo") + { + @Override + protected void onReAdd() + { + ; // I should call super, but since I don't, this should throw an exception + } + }; + brokenProbe.internalInitialize(); + page.add(brokenProbe); + fail("should have thrown exception"); + } catch (IllegalStateException e) + { + assertTrue(e.getMessage().contains("super.onReAdd")); + } + } + + private Component createNestedProbe() + { + return createContainer().add(createUninitializedProbe()); + } + + private MarkupContainer createContainer() + { + return new WebMarkupContainer("bar"); + } +}