org.apache.wicket.Component Maven / Gradle / Ivy
Show all versions of wicket-core Show documentation
/* * 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 java.io.Serializable; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Optional; import org.apache.wicket.ajax.IAjaxRegionMarkupIdProvider; import org.apache.wicket.application.IComponentInstantiationListener; import org.apache.wicket.authorization.Action; import org.apache.wicket.authorization.AuthorizationException; import org.apache.wicket.authorization.IAuthorizationStrategy; import org.apache.wicket.authorization.UnauthorizedActionException; import org.apache.wicket.authorization.strategies.page.SimplePageAuthorizationStrategy; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.core.request.handler.BookmarkableListenerRequestHandler; import org.apache.wicket.core.request.handler.ListenerRequestHandler; import org.apache.wicket.core.request.handler.PageAndComponentProvider; import org.apache.wicket.core.util.lang.WicketObjects; import org.apache.wicket.core.util.string.ComponentStrings; import org.apache.wicket.event.Broadcast; import org.apache.wicket.event.IEvent; import org.apache.wicket.event.IEventSink; import org.apache.wicket.event.IEventSource; import org.apache.wicket.feedback.FeedbackDelay; import org.apache.wicket.feedback.FeedbackMessage; import org.apache.wicket.feedback.FeedbackMessages; import org.apache.wicket.feedback.IFeedback; import org.apache.wicket.feedback.IFeedbackContributor; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.IMarkupFragment; import org.apache.wicket.markup.Markup; import org.apache.wicket.markup.MarkupCache; import org.apache.wicket.markup.MarkupElement; import org.apache.wicket.markup.MarkupException; import org.apache.wicket.markup.MarkupNotFoundException; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.markup.WicketTag; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.StringHeaderItem; import org.apache.wicket.markup.html.IHeaderContributor; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; import org.apache.wicket.markup.html.panel.DefaultMarkupSourcingStrategy; import org.apache.wicket.markup.html.panel.IMarkupSourcingStrategy; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IComponentAssignedModel; import org.apache.wicket.model.IComponentInheritedModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.IModelComparator; import org.apache.wicket.model.IWrapModel; import org.apache.wicket.protocol.http.WicketFilter; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; import org.apache.wicket.request.component.IRequestableComponent; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.response.StringResponse; import org.apache.wicket.settings.DebugSettings; import org.apache.wicket.settings.ExceptionSettings; import org.apache.wicket.util.IHierarchical; import org.apache.wicket.util.convert.IConverter; import org.apache.wicket.util.io.IClusterable; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.lang.Classes; import org.apache.wicket.util.string.PrependingStringBuffer; import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.value.ValueMap; import org.apache.wicket.util.visit.IVisitFilter; import org.apache.wicket.util.visit.IVisitor; import org.apache.wicket.util.visit.Visit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Component serves as the highest level abstract base class for all components. * *
*/ public boolean isEnabledInHierarchy() { if (getRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET)) { return getRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_VALUE); } final boolean state; Component parent = getParent(); if (parent != null && !parent.isEnabledInHierarchy()) { state = false; } else { state = isEnabled() && isEnableAllowed(); } setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET, true); setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_VALUE, state); return state; } /** * Says if the component is rendering currently. * * @return true if this component is rendering, false otherwise. */ public final boolean isRendering() { return getRequestFlag(RFLAG_PREPARED_FOR_RENDER) || getRequestFlag(RFLAG_RENDERING); } /** * Checks whether or not an {@link IRequestListener} can be invoked on this component. Usually components * deny these invocations if they are either invisible or disabled in hierarchy. **
* * @author Jonathan Locke * @author Chris Turner * @author Eelco Hillenius * @author Johan Compagner * @author Juergen Donnerstag * @author Igor Vaynberg (ivaynberg) */ public abstract class Component implements IClusterable, IConverterLocator, IRequestableComponent, IHeaderContributor, IHierarchical- Identity - 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 * does not have to be globally unique or unique within a Page's component hierarchy.
*- Hierarchy - A component has a parent which can be retrieved with {@link #getParent()}. * If a component is an instance of MarkupContainer, it may have children. In this way it has a * place in the hierarchy of components contained on a given page. *
** The path from the Page at the root of the component hierarchy to a given Component is simply the * concatenation with colon separators of each id along the way. For example, the path "a:b:c" would * refer to the component named "c" inside the MarkupContainer named "b" inside the container named * "a". The path to a component can be retrieved by calling {@link #getPath()}. To get a Component * path relative to the page that contains it, you can call {@link #getPageRelativePath()}.
- LifeCycle - Components participate in the following lifecycle phases: *
**
*- Construction - A Component is constructed with the Java language new operator. * Children may be added during construction if the Component is a MarkupContainer. * {@link IComponentInstantiationListener}s are notified of component instantiation. *
** {@link #onInitialize()} is called on the component as soon as the component is part of a page's * component tree. At this state the component is able to access its markup.
- Request Handling - An incoming request is processed by a protocol request handler such * as {@link WicketFilter}. An associated Application object creates {@link Session}, * {@link Request} and {@link Response} objects for use by a given Component in updating its model * and rendering a response. These objects are stored inside a container called {@link RequestCycle} * which is accessible via {@link Component#getRequestCycle()}. The convenience methods * {@link Component#getRequest()}, {@link Component#getResponse()} and * {@link Component#getSession()} provide easy access to the contents of this container.
*- Listener Invocation - If the request references an {@link IRequestListener} on an * existing Component (or one of its {@link Behavior}s, see below), that listener is notified, * allowing arbitrary user code to handle events such as link clicks or form submits. Although * arbitrary listeners are supported in Wicket, the need to implement a new class of listener is * unlikely for a web application and even the need to implement a listener interface directly is * highly discouraged. Instead, calls to listeners are routed through logic specific to the event, * resulting in calls to user code through other overridable methods. See {@link Form} for an * example of a component which listens for events via {@link IRequestListener}.
*- Rendering - Before a page or part of a page (in case of Ajax updates) is rendered, all * containing components are able to prepare for rendering via two hook methods: * {@link #onConfigure()} (regardless whether they are visible or not) and {@link #onBeforeRender()} * (if visible only) .
*
* A markup response is generated by the Component via {@link Component#render()}, which calls * subclass implementation code contained in {@link Component#onRender()}. Once this phase begins, a * Component becomes immutable. Attempts to alter the Component will result in a * WicketRuntimeException.- Detachment - Each request cycle finishes by detaching all touched components. * Subclasses should clean up their state by overriding {@link #onDetach()} or more specifically * {@link #detachModels()} if they keep references to models beside the default model.
*- Visibility - If a component is not visible (see {@link #setVisible(boolean)}) it will * not render a response (nor will their children).
*- Enabling - Component subclasses take into account their enabled state (see * {@link #setEnabled(boolean)} when rendering, and in case of a {@link FormComponent} will not not * update its model while the request is handled.
*- Models - The primary responsibility of a component is to use its model (an object that * implements {@link IModel}) to render a response in an appropriate markup language, such as HTML. * In addition, {@link FormComponent}s know how to update their models based on request information, * see {@link FormComponent#updateModel()}. Since the IModel interface is a wrapper around another * object, a convenience method {@link Component#getDefaultModelObject()} is provided to retrieve * the object from its IModel wrapper. A further convenience method, * {@link Component#getDefaultModelObjectAsString()}, is provided for the very common operation of * converting the wrapped object to a String.
*
* The component's model can be passed in the constructor or set via * {@link Component#setDefaultModel(IModel)}. In neither case a model can be created on demand with * {@link #initModel()}.
* Note that a component can have more models besides its default model.- Behaviors - You can add multiple {@link Behavior}s to any component if you need to * dynamically alter the behavior of components, e.g. manipulate attributes of the markup tag to * which a Component is attached. Behaviors take part in the component's lifecycle through various * callback methods.
*- Locale - The Locale for a Component is available through {@link #getLocale()}, which * delegates to its parent's locale, finally consulting the {@link Session}'s locale.
*- Style - The Session's style ("skin") is available through * {@link org.apache.wicket.Component#getStyle()}. Styles are intended to give a particular look to * all components or resources in a session that is independent of its Locale. For example, a style * might be a set of resources, including images and markup files, which gives the design look of * "ocean" to the user. If the Session's style is set to "ocean" and these resources are given names * suffixed with "_ocean", Wicket's resource management logic will prefer these resources to other * resources, such as default resources, which are not as good of a match.
*- Variation - Whereas styles are Session (user) specific, variations are component * specific. E.g. if the Style is "ocean" and {@link #getVariation()} returnss "NorthSea", than the * resources are given the names suffixed with "_ocean_NorthSea".
*- String Resources - Components can have associated String resources via the * Application's Localizer, which is available through the method {@link Component#getLocalizer()}. * The convenience methods {@link Component#getString(String key)} and * {@link Component#getString(String key, IModel model)} wrap the identical methods on the * Application Localizer for easy access in Components.
*- Feedback Messages - The {@link Component#debug(Serializable)}, * {@link Component#info(Serializable)}, {@link Component#warn(Serializable)}, * {@link Component#error(java.io.Serializable)} and {@link Component#fatal(Serializable)} methods * associate feedback messages with a Component. It is generally not necessary to use these methods * directly since Wicket validators automatically register feedback messages on Components. Feedback * message for a given Component can be retrieved with {@link Component#getFeedbackMessages}.
*- Versioning - Pages are the unit of versioning in Wicket, but fine-grained control of * which Components should participate in versioning is possible via the * {@link Component#setVersioned(boolean)} method. The versioning participation of a given Component * can be retrieved with {@link Component#isVersioned()}.
*- Page - The Page containing any given Component can be retrieved by calling * {@link Component#getPage()}. If the Component is not attached to a Page, an IllegalStateException * will be thrown. An equivalent method, {@link Component#findPage()} is available for special * circumstances where it might be desirable to get a null reference back instead.
*- Application - The {@link #getApplication()} method provides convenient access to the * {@link Application} for a Component.
*- AJAX support- Components can be re-rendered after the whole Page has been rendered at * least once by calling doRender().
*- Security - All components are subject to an {@link IAuthorizationStrategy} which * controls instantiation, visibility and enabling. See {@link SimplePageAuthorizationStrategy} for * a simple implementation.
*, IEventSink, IEventSource, IMetadataContext , IFeedbackContributor { /** Log. */ private static final Logger log = LoggerFactory.getLogger(Component.class); private static final long serialVersionUID = 1L; /** * Action used with IAuthorizationStrategy to determine whether a component is allowed to be * enabled. * * If enabling is authorized, a component may decide by itself (typically using it's enabled * property) whether it is enabled or not. If enabling is not authorized, the given component is * marked disabled, regardless its enabled property. *
* 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: * *
* * User u = (User)getModelObject(); * u.setName("got you there!"); * ** * 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. */ public static final Action ENABLE = new Action(Action.ENABLE); /** Separator for component paths */ public static final char PATH_SEPARATOR = ':'; /** Path segment that represents this component's parent */ public static final String PARENT_PATH = ".."; /** * Action used with IAuthorizationStrategy to determine whether a component and its children are * allowed to be rendered. ** There are two uses for this method: *
*
* */ public static final Action RENDER = new Action(Action.RENDER); /** meta data for user specified markup id */ private static final MetaDataKey- The 'normal' use is for controlling whether a component is rendered without having any * effect on the rest of the processing. If a strategy lets this method return 'false', then the * target component and its children will not be rendered, in the same fashion as if that * component had visibility property 'false'.
*- The other use is when a component should block the rendering of the whole page. So * instead of 'hiding' a component, what we generally want to achieve here is that we force the * user to logon/give-credentials for a higher level of authorization. For this functionality, * the strategy implementation should throw a {@link AuthorizationException}, which will then be * handled further by the framework.
*MARKUP_ID_KEY = new MetaDataKey<>() { private static final long serialVersionUID = 1L; }; /** meta data for user specified markup id */ private static final MetaDataKey FEEDBACK_KEY = new MetaDataKey<>() { private static final long serialVersionUID = 1L; }; /** Basic model IModelComparator implementation for normal object models */ private static final IModelComparator defaultModelComparator = new IModelComparator() { private static final long serialVersionUID = 1L; @Override public boolean compare(Component component, Object b) { final Object a = component.getDefaultModelObject(); if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } return a.equals(b); } }; /** True when a component is being auto-added */ private static final int FLAG_AUTO = 0x0001; /** Flag for escaping HTML in model strings */ private static final int FLAG_ESCAPE_MODEL_STRINGS = 0x0002; /** Boolean whether this component's model is inheritable. */ static final int FLAG_INHERITABLE_MODEL = 0x0004; /** Versioning boolean */ private static final int FLAG_VERSIONED = 0x0008; /** Visibility boolean */ private static final int FLAG_VISIBLE = 0x0010; /** Render tag boolean */ private static final int FLAG_RENDER_BODY_ONLY = 0x0020; /** Ignore attribute modifiers */ private static final int FLAG_IGNORE_ATTRIBUTE_MODIFIER = 0x0040; /** True when a component is enabled for model updates and is reachable. */ private static final int FLAG_ENABLED = 0x0080; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED1 = 0x0100; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED2 = 0x0200; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED3 = 0x0400; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED4 = 0x0800; /** Boolean whether this component was rendered at least once for tracking changes. */ private static final int FLAG_HAS_BEEN_RENDERED = 0x1000; /** * Internal indicator of whether this component may be rendered given the current context's * authorization. It overrides the visible flag in case this is false. Authorization is done * before trying to render any component (otherwise we would end up with a half rendered page in * the buffer) */ private static final int FLAG_IS_RENDER_ALLOWED = 0x2000; /** Whether or not the component should print out its markup id into the id attribute */ private static final int FLAG_OUTPUT_MARKUP_ID = 0x4000; /** * Output a placeholder tag if the component is not visible. This is useful in ajax mode to go * to visible(false) to visible(true) without the overhead of repainting a visible parent * container */ private static final int FLAG_PLACEHOLDER = 0x8000; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED5 = 0x10000; /** onInitialize called */ protected static final int FLAG_INITIALIZED = 0x20000; /** Set when a component is removed from the hierarchy */ private static final int FLAG_REMOVED = 0x40000; /** Reserved subclass-definable flag bit */ protected static final int FLAG_RESERVED8 = 0x80000; /** * Flag that determines whether the model is set. This is necessary because of the way we * represent component state ({@link #data}). We can't distinguish between model and behavior * using instanceof, because one object can implement both interfaces. Thus we need this flag - * when the flag is set, first object in {@link #data} is always model. */ private static final int FLAG_MODEL_SET = 0x100000; /** * Flag that restricts visibility of a component when set to true. This is usually used when a * component wants to restrict visibility of another component. Calling * {@link #setVisible(boolean)} on a component does not always have the desired effect because * isVisible() can be overwritten thus this flag offers an alternative that should always work. */ private static final int FLAG_VISIBILITY_ALLOWED = 0x40000000; /** * The name of attribute that will hold markup id */ private static final String MARKUP_ID_ATTR_NAME = "id"; /** * Meta data key for line precise error logging for the moment of addition. Made package private * for access in {@link MarkupContainer} and {@link Page} */ static final MetaDataKey ADDED_AT_KEY = new MetaDataKey<>() { private static final long serialVersionUID = 1L; }; /** * meta data key for line precise error logging for the moment of construction. Made package * private for access in {@link Page} */ static final MetaDataKey CONSTRUCTED_AT_KEY = new MetaDataKey<>() { private static final long serialVersionUID = 1L; }; /** Component flags. See FLAG_* for possible non-exclusive flag values. */ private int flags = FLAG_VISIBLE | FLAG_ESCAPE_MODEL_STRINGS | FLAG_VERSIONED | FLAG_ENABLED | FLAG_IS_RENDER_ALLOWED | FLAG_VISIBILITY_ALLOWED | FLAG_RESERVED5 /* page's stateless hint */; // @formatter:off private static final short RFLAG_ENABLED_IN_HIERARCHY_VALUE = 0x1; private static final short RFLAG_ENABLED_IN_HIERARCHY_SET = 0x2; private static final short RFLAG_VISIBLE_IN_HIERARCHY_VALUE = 0x4; private static final short RFLAG_VISIBLE_IN_HIERARCHY_SET = 0x8; /** onconfigure has been called */ 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; protected static final short RFLAG_CONTAINER_DEQUEING = 0x80; private static final short RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED = 0x100; /** * Flag that makes we are in before-render callback phase Set after component.onBeforeRender is * invoked (right before invoking beforeRender on children) */ private static final short RFLAG_RENDERING = 0x200; private static final short RFLAG_PREPARED_FOR_RENDER = 0x400; private static final short RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED = 0x800; private static final short RFLAG_DETACHING = 0x1000; /** True when a component is being removed from the hierarchy */ private static final short RFLAG_REMOVING_FROM_HIERARCHY = 0x2000; /** * This flag tracks if removals have been set on this component. Clearing this key is an * expensive operation. With this flag this expensive call can be avoided. */ protected static final short RFLAG_CONTAINER_HAS_REMOVALS = 0x4000; private static final short RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED = (short) 0x8000; // @formatter:on /** * Flags that only keep their value during the request. Useful for cache markers, etc. At the * end of the request the value of this variable is reset to 0 */ private transient short requestFlags = 0; /** Component id. */ private final String id; /** Any parent container. */ private MarkupContainer parent; /** * Instead of remembering the whole markupId, we just remember the number for this component so * we can "reconstruct" the markupId on demand. While this could be part of {@link #data}, * profiling showed that having it as separate property consumes less memory. */ int generatedMarkupId = -1; /** Must only be used by auto components */ private transient IMarkupFragment markup; /** * Will be re-created instead of persisted when session is replicated. Markup sourcing strategy * are typically stateless (but don't have to). */ private transient IMarkupSourcingStrategy markupSourcingStrategy; /** * The object that holds the component state. * * What's stored here depends on what attributes are set on component. Data can contains * combination of following attributes: *
*
* If there is only one attribute set (i.e. model or MetaDataEntry([]) or one behavior), the * #data object points directly to value of that attribute. Otherwise the data is of type * Object[] where the attributes are ordered as specified above. *- Model (indicated by {@link #FLAG_MODEL_SET}) *
- MetaDataEntry (optionally {@link MetaDataEntry}[] if more metadata entries are present) * *
- {@link Behavior}(s) added to component. The behaviors are not stored in separate array, * they are part of the {@link #data} array (this is in order to save the space of the pointer * to an empty array as most components have no behaviours). - FIXME - explain why - is this * correct? *
*/ Object data = null; final int data_start() { return getFlag(FLAG_MODEL_SET) ? 1 : 0; } final int data_length() { if (data == null) { return 0; } else if (data instanceof Object[] && !(data instanceof MetaDataEntry>[])) { return ((Object[])data).length; } else { return 1; } } final Object data_get(int index) { if (data == null) { return null; } else if (data instanceof Object[] && !(data instanceof MetaDataEntry>[])) { Object[] array = (Object[])data; return index < array.length ? array[index] : null; } else if (index == 0) { return data; } else { return null; } } final void data_set(int index, Object object) { if (index > data_length() - 1) { throw new IndexOutOfBoundsException("can not set data at " + index + " when data_length() is " + data_length()); } else if (index == 0 && !(data instanceof Object[] && !(data instanceof MetaDataEntry>[]))) { data = object; } else { Object[] array = (Object[])data; array[index] = object; } } final void data_add(Object object) { data_insert(-1, object); } final void data_insert(int position, Object object) { int currentLength = data_length(); if (position == -1) { position = currentLength; } if (position > currentLength) { throw new IndexOutOfBoundsException("can not insert data at " + position + " when data_length() is " + currentLength); } if (currentLength == 0) { data = object; } else if (currentLength == 1) { Object[] array = new Object[2]; if (position == 0) { array[0] = object; array[1] = data; } else { array[0] = data; array[1] = object; } data = array; } else { Object[] array = new Object[currentLength + 1]; Object[] current = (Object[])data; int after = currentLength - position; if (position > 0) { System.arraycopy(current, 0, array, 0, position); } array[position] = object; if (after > 0) { System.arraycopy(current, position, array, position + 1, after); } data = array; } } final void data_remove(int position) { int currentLength = data_length(); if (position > currentLength - 1) { throw new IndexOutOfBoundsException(); } else if (currentLength == 1) { data = null; } else if (currentLength == 2) { Object[] current = (Object[])data; if (position == 0) { data = current[1]; } else { data = current[0]; } } else { Object[] current = (Object[])data; data = new Object[currentLength - 1]; if (position > 0) { System.arraycopy(current, 0, data, 0, position); } if (position != currentLength - 1) { final int left = currentLength - position - 1; System.arraycopy(current, position + 1, data, position, left); } } } /** * 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 * Thrown if the component has been given a null id. */ public Component(final String id) { this(id, null); } /** * 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. */ public Component(final String id, final IModel> model) { checkId(id); this.id = id; init(); Application application = getApplication(); application.getComponentInstantiationListeners().onInstantiation(this); final DebugSettings debugSettings = application.getDebugSettings(); if (debugSettings.isLinePreciseReportingOnNewComponentEnabled() && debugSettings.getComponentUseCheck()) { setMetaData(CONSTRUCTED_AT_KEY, ComponentStrings.toString(this, new MarkupException("constructed"))); } if (model != null) { setModelImpl(wrap(model)); } } /** * Let subclasses initialize this instance, before constructors are executed.
* Components like Panel and Border should return the "calling" markup fragment, e.g. *
* This method is intentionally not declared protected, to limit overriding to classes in * this package. */ void init() { } /** * Get the Markup associated with the Component. If not subclassed, the parent container is * asked to return the markup of this child component. *<span wicket:id="myPanel">body</span>
. You may use * 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() { // Markup already determined or preset? if (markup != null) { return markup; } // No parent, than check associated markup files if (parent == null) { // Must be a MarkupContainer to have associated markup file if (this instanceof MarkupContainer) { MarkupContainer container = (MarkupContainer)this; Markup associatedMarkup = container.getAssociatedMarkup(); if (associatedMarkup != null) { markup = associatedMarkup; return markup; } } // Don't know how to find the markup throw new MarkupNotFoundException( "Can not determine Markup. Component is not yet connected to a parent. " + toString()); } // Ask the parent for find the markup for me markup = parent.getMarkup(this); return markup; } /** * @return The 'id' attribute from the associated markup tag */ public final String getMarkupIdFromMarkup() { ComponentTag tag = getMarkupTag(); if (tag != null) { String id = tag.getAttribute("id"); if (Strings.isEmpty(id) == false) { return id.trim(); } } return null; } /** * 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 components are using it. You may also * it if you subclassed getMarkup(). * * @param markup */ public final Component setMarkup(final IMarkupFragment markup) { this.markup = markup; return this; } /** * Called on all components before any component is rendered. This method * should be used to configure such things as visibility and enabled flags. ** Overrides must call {@code super.onConfigure()}, usually before any other code *
* NOTE: Component hierarchy should not be modified inside this method, instead it should be * done in {@link #onBeforeRender()} *
* NOTE: Why this method is preferrable to directly overriding {@link #isVisible()} and * {@link #isEnabled()}? Because those methods are called multiple times even for processing of * a single request. If they contain expensive logic they can slow down the response time of the * entire page. Further, overriding those methods directly on form components may lead to * inconsistent or unexpected state depending on when those methods are called in the form * processing workflow. It is a better practice to push changes to state rather than pull. *
* NOTE: If component's visibility or another property depends on another component you may call * {@code other.configure()} followed by {@code other.isVisible()} as mentioned in * {@link #configure()} javadoc. *
* NOTE: Why should {@link #onBeforeRender()} not be used for this? Because if a component's * visibility is controlled inside {@link #onBeforeRender()}, once invisible the component will * never become visible again. */ protected void onConfigure() { setRequestFlag(RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED, true); } /** * This method is meant to be used as an alternative to initialize components. Usually the * component's constructor is used for this task, but sometimes a component cannot be * initialized in isolation, it may need to access its parent component or its markup in order * to fully initialize. This method is invoked once per component's lifecycle when a path exists * from this component to the {@link Page} thus providing the component with an atomic callback * when the component's environment is built out. *
* Overrides must call super#{@link #onInitialize()}. Usually this should be the first thing an * override does, much like a constructor. *
** Parent containers are guaranteed to be initialized before their children *
* ** It is safe to use {@link #getPage()} in this method *
* ** NOTE:The timing of this call is not precise, the contract is that it is called sometime * before {@link Component#onBeforeRender()}. *
* */ protected void onInitialize() { setRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED, true); } /** * Checks if the component has been initialized - {@link #onInitialize()} has been called * * @return {@code true} if component has been initialized */ public final boolean isInitialized() { return getFlag(FLAG_INITIALIZED); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Used to call {@link #onInitialize()} */ public void internalInitialize() { fireInitialize(); } /** * Used to call {@link #onInitialize()} */ final void fireInitialize() { if (!getFlag(FLAG_INITIALIZED)) { setFlag(FLAG_INITIALIZED, true); setRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED, false); onInitialize(); verifySuperCall("onInitialize", RFLAG_INITIALIZE_SUPER_CALL_VERIFIED); getApplication().getComponentInitializationListeners().onInitialize(this); } else if (getFlag(FLAG_REMOVED)) { setFlag(FLAG_REMOVED, false); setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, false); onReAdd(); verifySuperCall("onReAdd", RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED); } } /** * Called on every component after the page is rendered. Calls hook {@link #onAfterRender()}. */ final void afterRender() { setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); try { setRequestFlag(RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED, false); onAfterRender(); getApplication().getComponentOnAfterRenderListeners().onAfterRender(this); verifySuperCall("onAfterRender", RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED); } finally { // this flag must always be set to false. setRequestFlag(RFLAG_RENDERING, false); } } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Called on all components before any component is rendered. Calls hooks * {@link #configure()} and (if visible) {@link #onBeforeRender()} * and delegates to {@link #beforeRender()} of all child components. */ public final void beforeRender() { if (this instanceof IFeedback) { Optionaldelay = FeedbackDelay.get(getRequestCycle()); if (delay.isPresent()) { delay.get().postpone((IFeedback)this); return; } } configure(); if ((determineVisibility()) && !getRequestFlag(RFLAG_RENDERING) && !getRequestFlag(RFLAG_PREPARED_FOR_RENDER)) { try { setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, false); Application application = getApplication(); application.getComponentPreOnBeforeRenderListeners().onBeforeRender(this); onBeforeRender(); application.getComponentPostOnBeforeRenderListeners().onBeforeRender(this); verifySuperCall("onBeforeRender", RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED); } catch (RuntimeException ex) { setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); throw ex; } } } /** * Triggers {@link #onConfigure()} to be invoked on this component if it has not already during * this request. * * This method should be invoked before any calls to {@link #isVisible()} or * {@link #isEnabled()}. Usually this method will be called by the framework before the * component is rendered and so users should not need to call it; however, in cases where * 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: * *
* 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 * setVisible(source.isVisible()); * } * } ** * */ public final void configure() { if (!getRequestFlag(RFLAG_CONFIGURED)) { clearEnabledInHierarchyCache(); clearVisibleInHierarchyCache(); setRequestFlag(RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED, false); onConfigure(); verifySuperCall("onConfigure", RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED); for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { behavior.onConfigure(this); } } // check authorization setRenderAllowed(); internalOnAfterConfigure(); getApplication().getComponentOnConfigureListeners().onConfigure(this); setRequestFlag(RFLAG_CONFIGURED, true); } } /** * Verify super calls of an overridden hook method. */ private final void verifySuperCall(String method, short flag) { if (!getRequestFlag(flag)) { throw new IllegalStateException(String.format("%s() in the hierarchy of %s has not called super.%s()", method, getClass().getName(), method)); } setRequestFlag(flag, false); } /** * Called after the {@link #onConfigure()}, but before {@link #onBeforeRender()} */ void internalOnAfterConfigure() { } /** * Redirects to any intercept page previously specified by a call to {@link #redirectToInterceptPage(Page)}. * 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: * ** add(new Link("login") * { * protected void onClick() * { * if (authenticate()) * { * continueToOriginalDestination(); * // if we reach this line there was no intercept page, so go to home page * setResponsePage(WelcomePage.class); * } * } * }); * * @see Component#redirectToInterceptPage(Page) */ public final void continueToOriginalDestination() { RestartResponseAtInterceptPageException.continueToOriginalDestination(); } /** * Clears any data about previously intercepted page. */ public final void clearOriginalDestination() { RestartResponseAtInterceptPageException.clearOriginalDestination(); } /** * Registers a debug feedback message for this component * * @param message * The feedback message */ @Override public final void debug(final Serializable message) { getFeedbackMessages().debug(this, message); addStateChange(); } /** * Signals this Component that it is removed from the Component hierarchy. */ final void internalOnRemove() { setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, true); onRemove(); setFlag(FLAG_REMOVED, true); if (getRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY)) { throw new IllegalStateException(Component.class.getName() + " has not been properly removed from hierarchy. Something in the hierarchy of " + getClass().getName() + " has not called super.onRemove() in the override of onRemove() method"); } Behaviors.onRemove(this); removeChildren(); } /** * Detaches the component. This is called at the end of the request for all the pages that are * touched in that request. */ @Override public final void detach() { try { setRequestFlag(RFLAG_DETACHING, true); onDetach(); if (getRequestFlag(RFLAG_DETACHING)) { throw new IllegalStateException(Component.class.getName() + " has not been properly detached. Something in the hierarchy of " + getClass().getName() + " has not called super.onDetach() in the override of onDetach() method"); } // always detach models because they can be attached without the // component. eg component has a compoundpropertymodel and one of its // children component's getmodelobject is called detachModels(); // detach any behaviors Behaviors.detach(this); } catch (Exception x) { throw new WicketRuntimeException("An error occurred while detaching component: " + toString(true), x); } // always detach children because components can be attached // independently of their parents detachChildren(); // reset the model to null when the current model is a IWrapModel and // the model that created it/wrapped in it is a IComponentInheritedModel // The model will be created next time. if (getFlag(FLAG_INHERITABLE_MODEL)) { setModelImpl(null); setFlag(FLAG_INHERITABLE_MODEL, false); } clearEnabledInHierarchyCache(); clearVisibleInHierarchyCache(); // clear request flags but keep super call verifications WICKET-5417 requestFlags &= (RFLAG_INITIALIZE_SUPER_CALL_VERIFIED | RFLAG_ON_CONFIGURE_SUPER_CALL_VERIFIED | RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED); detachFeedback(); internalDetach(); // notify any detach listener IDetachListener detachListener = getApplication().getFrameworkSettings() .getDetachListener(); if (detachListener != null) { detachListener.onDetach(this); } } private void detachFeedback() { FeedbackMessages feedback = getMetaData(FEEDBACK_KEY); if (feedback != null) { feedback.clear(getApplication().getApplicationSettings() .getFeedbackMessageCleanupFilter()); if (feedback.isEmpty()) { setMetaData(FEEDBACK_KEY, null); } else { feedback.detach(); } } } /** * Removes the cached markup at the end of the request. For the next request it will be get * either from the parent's markup or from {@link MarkupCache}. */ private void internalDetach() { markup = null; } /** * Detaches all models */ public void detachModels() { // Detach any detachable model from this component detachModel(); } /** * Registers an error feedback message for this component * * @param message * The feedback message */ @Override public final void error(final Serializable message) { getFeedbackMessages().error(this, message); addStateChange(); } /** * Registers a fatal feedback message for this component * * @param message * The feedback message */ @Override public final void fatal(final Serializable message) { getFeedbackMessages().fatal(this, message); addStateChange(); } /** * Finds the first container parent of this component of the given class. * * @param* 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 * found */ public final Z findParent(final Class c) { // Start with immediate parent MarkupContainer current = parent; // Walk up containment hierarchy while (current != null) { // Is current an instance of this class? if (c.isInstance(current)) { return c.cast(current); } // Check parent current = current.getParent(); } // Failed to find component return null; } /** * @return The nearest markup container with associated markup */ public final MarkupContainer findParentWithAssociatedMarkup() { MarkupContainer container = parent; while (container != null) { if (container.getAssociatedMarkup() != null) { return container; } container = container.getParent(); } // This should never happen since Page always has associated markup throw new WicketRuntimeException("Unable to find parent with associated markup"); } /** * 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 */ public final Application getApplication() { return Application.get(); } /** * @return A path of the form [page-class-name]:[page-relative-path] * @see Component#getPageRelativePath() */ public final String getClassRelativePath() { return getClass().getName() + PATH_SEPARATOR + getPageRelativePath(); } /** * Get the converter that should be used by this component, delegates to * {@link #createConverter(Class)} and then to the application's * {@link IConverterLocator}. * * @param type * The type to convert to * * @return The converter that should be used by this component */ @SuppressWarnings("unchecked") @Override public IConverter getConverter(Class type) { IConverter> converter = createConverter(type); if (converter != null) { return (IConverter ) converter; } return getApplication().getConverterLocator().getConverter(type); } /** * Factory method for converters to be used by this component, * returns {@code null} by default. * * @param type * The type to convert to * * @return a converter to be used by this component */ protected IConverter> createConverter(Class> type) { return null; } /** * Gets whether model strings should be escaped. * * @return Returns whether model strings should be escaped */ public final boolean getEscapeModelStrings() { return getFlag(FLAG_ESCAPE_MODEL_STRINGS); } /** * Gets the id of this component. * * @return The id of this component */ @Override public String getId() { return id; } /** * @return Innermost model for this component */ public final IModel> getInnermostModel() { return getInnermostModel(getDefaultModel()); } /** * 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() */ public Locale getLocale() { if (parent != null) { return parent.getLocale(); } return getSession().getLocale(); } /** * Convenience method to provide easy access to the localizer object within any component. * * @return The localizer object */ public final Localizer getLocalizer() { return getApplication().getResourceSettings().getLocalizer(); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Get the first component tag in the associated markup * * @return first component tag */ protected final ComponentTag getMarkupTag() { IMarkupFragment markup = getMarkup(); if (markup != null) { for (int i = 0; i < markup.size(); i++) { MarkupElement elem = markup.get(i); if (elem instanceof ComponentTag) { return (ComponentTag)elem; } } } return null; } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Get a copy of the markup's attributes which are associated with the component. * * Modifications to the map returned don't change the tags attributes. It is just a copy. *
* 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() { ComponentTag tag = getMarkupTag(); if (tag != null) { ValueMap attrs = new ValueMap(tag.getAttributes()); attrs.makeImmutable(); return attrs; } return ValueMap.EMPTY_MAP; } /** * Get the markupId * * @return MarkupId */ public final Object getMarkupIdImpl() { if (generatedMarkupId != -1) { return generatedMarkupId; } String id = getMetaData(MARKUP_ID_KEY); // if still no markup id is found, and the component has been attached to a page, try to // retrieve the id from the markup file. if (id == null && findPage() != null) { id = getMarkupIdFromMarkup(); } return id; } /** * Retrieves id by which this component is represented within the markup. This is either the id * attribute set explicitly via a call to {@link #setMarkupId(String)}, id attribute defined in * the markup, or an automatically generated id - in that order. *
* If no id is set and
createIfDoesNotExist
is false, this method will return null. * Otherwise it will generate an id value which by default will be unique in the page. This is * the preferred way as there is no chance of id collision. This will also enable * {@link #setOutputMarkupId(boolean)}. ** *
* 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
null
should be returned. * * @return markup id of the component */ public String getMarkupId(boolean createIfDoesNotExist) { IMarkupIdGenerator markupIdGenerator = getApplication().getMarkupSettings().getMarkupIdGenerator(); String markupId = markupIdGenerator.generateMarkupId(this, createIfDoesNotExist); return markupId; } /** * Retrieves id by which this component is represented within the markup. This is either the id * attribute set explicitly via a call to {@link #setMarkupId(String)}, id attribute defined in * the markup, or an automatically generated id - in that order. ** If no explicit id is set this function will generate an id value that will be unique in the * page. This is the preferred way as there is no chance of id collision. This will also enable * {@link #setOutputMarkupId(boolean)}. *
* 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() { return getMarkupId(true); } /** * Gets metadata for this component using the given key. * * @param
* 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 * @see MetaDataKey */ @Override public final M getMetaData(final MetaDataKey key) { return key.get(getMetaData()); } /** * Gets the meta data entries for this component as an array of {@link MetaDataEntry} objects. * * @return the meta data entries for this component */ private MetaDataEntry>[] getMetaData() { MetaDataEntry>[] metaData = null; // index where we should expect the entry int index = getFlag(FLAG_MODEL_SET) ? 1 : 0; int length = data_length(); if (index < length) { Object object = data_get(index); if (object instanceof MetaDataEntry>[]) { metaData = (MetaDataEntry>[])object; } else if (object instanceof MetaDataEntry) { metaData = new MetaDataEntry[] { (MetaDataEntry>)object }; } } return metaData; } /** * Gets the model. It returns the object that wraps the backing model. * * @return The model */ public final IModel> getDefaultModel() { IModel> model = getModelImpl(); // If model is null if (model == null) { // give subclass a chance to lazy-init model model = initModel(); setModelImpl(model); } return model; } /** * 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() { final IModel> model = getDefaultModel(); if (model != null) { try { // Get model value for this component. return model.getObject(); } catch (Exception ex) { // wrap the exception so that it brings info about the component WicketRuntimeException rex = new WicketRuntimeException( "An error occurred while getting the model object for Component: " + this.toString(true), ex); throw rex; } } return null; } /** * Gets a model object as a string. Depending on the "escape model strings" flag of the * component, the string is either HTML escaped or not. "HTML escaped" meaning that only HTML * 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() { return getDefaultModelObjectAsString(getDefaultModelObject()); } /** * Gets a model object as a string. Depending on the "escape model strings" flag of the * component, the string is either HTML escaped or not. "HTML escaped" meaning that only HTML * 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 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public final String getDefaultModelObjectAsString(final Object modelObject) { if (modelObject != null) { // Get converter final Class> objectClass = modelObject.getClass(); final IConverter converter = getConverter(objectClass); // Model string from property final String modelString = converter.convertToString(modelObject, getLocale()); if (modelString != null) { // If we should escape the markup if (getFlag(FLAG_ESCAPE_MODEL_STRINGS)) { // Escape HTML sensitive characters only. Not all none-ascii chars return Strings.escapeMarkup(modelString, false, false).toString(); } return modelString; } } return ""; } /** * 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() { return getFlag(FLAG_OUTPUT_MARKUP_ID); } /** * 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() { return getFlag(FLAG_PLACEHOLDER); } /** * Gets the page holding this component. * * @return The page holding this component * @throws WicketRuntimeException * Thrown if component is not yet attached to a Page. * @see #findPage() */ @Override public final Page getPage() { // Search for nearest Page final Page page = findPage(); // If no Page was found if (page == null) { // Give up with a nice exception throw new WicketRuntimeException("No Page found for component: " + this.toString(true) + ". You probably forgot to add it to its parent component."); } return page; } /** * 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 public final String getPageRelativePath() { return Strings.afterFirstPathComponent(getPath(), PATH_SEPARATOR); } /** * Gets any parent container, or null if there is none. * * @return Any parent container, or null if there is none */ @Override public final MarkupContainer getParent() { return parent; } /** * Gets this component's path. * * @return Colon separated path to this component in the component hierarchy */ public final String getPath() { final PrependingStringBuffer buffer = new PrependingStringBuffer(32); for (Component c = this; c != null; c = c.getParent()) { if (buffer.length() > 0) { buffer.prepend(PATH_SEPARATOR); } buffer.prepend(c.getId()); } return buffer.toString(); } /** * 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() { return getFlag(FLAG_RENDER_BODY_ONLY); } /** * @return The request for this component's active request cycle */ public final Request getRequest() { RequestCycle requestCycle = getRequestCycle(); if (requestCycle == null) { // Happens often with WicketTester when one forgets to call // createRequestCycle() throw new WicketRuntimeException("No RequestCycle is currently set!"); } return requestCycle.getRequest(); } /** * Gets the active request cycle for this component * * @return The request cycle */ public final RequestCycle getRequestCycle() { return RequestCycle.get(); } /** * @return The response for this component's active request cycle */ public final Response getResponse() { return getRequestCycle().getResponse(); } /** * Gets the current Session object. * * @return The Session that this component is in */ public Session getSession() { return Session.get(); } /** * @return Size of this Component in bytes. Returns {@code 0} - if the size cannot be calculated for some reason */ public long getSizeInBytes() { final MarkupContainer originalParent = parent; parent = null; long size = 0; try { size = WicketObjects.sizeof(this); } catch (Exception e) { log.error("Exception getting size for component " + this, e); } parent = originalParent; return size; } /** * @param key * Key of string resource in property file * @return The String * @see Localizer */ public final String getString(final String key) { return getString(key, null); } /** * @param key * The resource key * @param model * The model * @return The formatted string * @see Localizer */ public final String getString(final String key, final IModel> model) { return getLocalizer().getString(key, this, model); } /** * @param key * The resource key * @param model * The model * @param defaultValue * A default value if the string cannot be found * @return The formatted string * @see Localizer */ public final String getString(final String key, final IModel> model, final String defaultValue) { return getLocalizer().getString(key, this, model, defaultValue); } /** * 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() { Session session = getSession(); if (session == null) { throw new WicketRuntimeException("Wicket Session object not available"); } return session.getStyle(); } /** * 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() { if (parent != null) { return parent.getVariation(); } return null; } /** * 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() { return getFlag(FLAG_HAS_BEEN_RENDERED); } /** * Gets feedback messages for this component. This method will instantiate a * {@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() { FeedbackMessages messages = getMetaData(FEEDBACK_KEY); if (messages == null) { messages = new FeedbackMessages(); setMetaData(FEEDBACK_KEY, messages); } return messages; } /** * @return True if this component has an error message */ public final boolean hasErrorMessage() { FeedbackMessages messages = getMetaData(FEEDBACK_KEY); if (messages == null) { return false; } return messages.hasMessage(FeedbackMessage.ERROR); } /** * @return True if this component has some kind of feedback message * */ public final boolean hasFeedbackMessage() { FeedbackMessages messages = getMetaData(FEEDBACK_KEY); if (messages == null) { return false; } return messages.size() > 0; } /** * Registers an informational feedback message for this component * * @param message * The feedback message */ @Override public final void info(final Serializable message) { getFeedbackMessages().info(this, message); addStateChange(); } /** * Registers an success feedback message for this component * * @param message * The feedback message */ @Override public final void success(final Serializable message) { getFeedbackMessages().success(this, message); addStateChange(); } /** * Authorizes an action for a component. * * @param action * The action to authorize * @return True if the action is allowed * @throws AuthorizationException * Can be thrown by implementation if action is unauthorized */ public final boolean isActionAuthorized(Action action) { IAuthorizationStrategy authorizationStrategy = getSession().getAuthorizationStrategy(); if (authorizationStrategy != null) { return authorizationStrategy.isActionAuthorized(this, action); } return true; } /** * @return true if this component is authorized to be enabled, false otherwise */ public final boolean isEnableAllowed() { return isActionAuthorized(ENABLE); } /** * 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() { return getFlag(FLAG_ENABLED); } /** * Checks the security strategy if the {@link Component#RENDER} action is allowed on this * component * * @return true if {@link Component#RENDER} action is allowed, false otherwise */ public final boolean isRenderAllowed() { return getFlag(FLAG_IS_RENDER_ALLOWED); } /** * 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() { if ((isVisibleInHierarchy() && isEnabledInHierarchy()) == false && canCallListener() == false) { // the component is either invisible or disabled and it can't call listeners // then pretend the component is stateless return true; } if (!getStatelessHint()) { return false; } for (Behavior behavior : getBehaviors()) { if (!behavior.getStatelessHint(this)) { return false; } } return true; } /** * @return {@code true} if this component should notify its holding page about changes in its * state. If a {@link Page} is not versioned then it wont track changes in its * components and will use the same {@link Page#getPageId()} during its lifetime */ public boolean isVersioned() { // Is the component itself versioned? if (!getFlag(FLAG_VERSIONED)) { return false; } else { // If there's a parent and this component is versioned if (parent != null) { // Check if the parent is unversioned. If any parent // (recursively) is unversioned, then this component is too if (!parent.isVersioned()) { return false; } } return true; } } /** * Gets whether this component and any children are visible. * * WARNING: this method can be called multiple times during a request. If you override this * method, it is a good idea to keep it cheap in terms of processing. Alternatively, you can * call {@link #setVisible(boolean)}. *
* * @return True if component and any children are visible */ public boolean isVisible() { return getFlag(FLAG_VISIBLE); } /** * 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() { if (getRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET)) { return getRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_VALUE); } final boolean state; Component parent = getParent(); if (parent != null && !parent.isVisibleInHierarchy()) { state = false; } else { state = determineVisibility(); } setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET, true); setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_VALUE, state); return state; } /** * 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. */ public final void markRendering(boolean setRenderingFlag) { internalMarkRendering(setRenderingFlag); } /** * Called to indicate that the model content for this component has been changed */ public final void modelChanged() { // Call user code internalOnModelChanged(); onModelChanged(); } /** * Called to indicate that the model content for this component is about to change */ public final void modelChanging() { checkHierarchyChange(this); // Call user code onModelChanging(); // Tell the page that our model changed final Page page = findPage(); if (page != null) { page.componentModelChanging(this); } } /** * Redirects browser to an intermediate page such as a sign-in page. The current request's URL * is saved for future use by method {@link #continueToOriginalDestination()}; only use this method when * you plan to continue to the current URL at some later time; otherwise just set a new response page. * * @param page * The sign in page * * @see #setResponsePage(Class) * @see #setResponsePage(IRequestablePage) * @see #setResponsePage(Class, PageParameters) * @see Component#continueToOriginalDestination() */ public final void redirectToInterceptPage(final Page page) { throw new RestartResponseAtInterceptPageException(page); } /** * Removes this component from its parent. It's important to remember that a component that is * removed cannot be referenced from the markup still. *
* You must not use this method in your callback to any of the * {@link MarkupContainer#visitChildren(IVisitor)} methods. See WICKET-3329. */ public final void remove() { if (parent == null) { throw new IllegalStateException("Cannot remove " + this + " from null parent!"); } parent.remove(this); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! *
* Renders this component as a part of a response - the caller has to * make sure that this component is prepared for render. * * @see #beforeRender() */ public final void renderPart() { Page page = getPage(); page.startComponentRender(this); markRendering(true); render(); page.endComponentRender(this); } /** * Render this component and all its children. Always calls hook {@link #onAfterRender()} * regardless of any exception. */ public final void render() { if (isAuto()) { // auto components are prepared when rendered beforeRender(); } // Do the render RuntimeException exception = null; try { setRequestFlag(RFLAG_RENDERING, true); internalRender(); } catch (final RuntimeException ex) { // Remember it as the originating exception exception = ex; } finally { try { // Cleanup afterRender(); } catch (RuntimeException ex2) { // Only remember it if not already another exception happened if (exception == null) { exception = ex2; } } } // Re-throw if needed if (exception != null) { throw exception; } } /** * Performs a render of this component as part of a Page level render process. */ private void internalRender() { // Make sure there is a markup available for the Component IMarkupFragment markup = getMarkup(); if (markup == null) { throw new MarkupNotFoundException("Markup not found for Component: " + toString()); } // MarkupStream is an Iterator for the markup MarkupStream markupStream = new MarkupStream(markup); MarkupElement elem = markup.get(0); if (elem instanceof ComponentTag) { // Guarantee that the markupStream is set and determineVisibility not yet tested // See WICKET-2049 ((ComponentTag)elem).onBeforeRender(this, markupStream); } // Determine if component is visible using it's authorization status // and the isVisible property. if (determineVisibility()) { setFlag(FLAG_HAS_BEEN_RENDERED, true); // Rendering is beginning if (log.isDebugEnabled()) { log.debug("Begin render {}", this); } try { notifyBehaviorsComponentBeforeRender(); onRender(); notifyBehaviorsComponentRendered(); // Component has been rendered rendered(); } catch (RuntimeException ex) { onException(ex); } if (log.isDebugEnabled()) { log.debug("End render {}", this); } } // elem is null when rendering a page else if ((elem != null) && (elem instanceof ComponentTag)) { if (getFlag(FLAG_PLACEHOLDER)) { renderPlaceholderTag(((ComponentTag)elem).mutable(), getResponse()); } } } /** * Called when a runtime exception is caught during the render process * * @param ex * The exception caught. */ private void onException(final RuntimeException ex) { // Call each behaviors onException() to allow the // behavior to clean up for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { try { behavior.onException(this, ex); } catch (Exception ex2) { log.error("Error while cleaning up after exception", ex2); } } } // Re-throw the exception throw ex; } /** * Renders a placeholder tag for the component when it is invisible and * {@link #setOutputMarkupPlaceholderTag(boolean)} has been called with
true
. * * @param tag * component tag * @param response * response */ protected void renderPlaceholderTag(final ComponentTag tag, final Response response) { String name = Strings.isEmpty(tag.getNamespace()) ? tag.getName() : tag.getNamespace() + ':' + tag.getName(); // prefer concatenation over String#format() for performance response.write( "<" + name + " id=\"" + getAjaxRegionMarkupId() + "\" hidden=\"\" data-wicket-placeholder=\"\">" + name + ">"); } /** * 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() { String markupId = null; for (Behavior behavior : getBehaviors()) { if (behavior instanceof IAjaxRegionMarkupIdProvider && behavior.isEnabled(this)) { markupId = ((IAjaxRegionMarkupIdProvider)behavior).getAjaxRegionMarkupId(this); break; } } if (markupId == null) { if (this instanceof IAjaxRegionMarkupIdProvider) { markupId = ((IAjaxRegionMarkupIdProvider)this).getAjaxRegionMarkupId(this); } } if (markupId == null) { markupId = getMarkupId(); } return markupId; } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! ** Renders the component at the current position in the given markup stream. The method * onComponentTag() is called to allow the component to mutate the start tag. The method * onComponentTagBody() is then called to permit the component to render its body. */ protected final void internalRenderComponent() { final IMarkupFragment markup = getMarkup(); if (markup == null) { throw new MarkupException("Markup not found. Component: " + toString()); } final MarkupStream markupStream = new MarkupStream(markup); // Get mutable copy of next tag final ComponentTag openTag = markupStream.getTag(); final ComponentTag tag = openTag.mutable(); // call application-wide tag listeners getApplication().getOnComponentTagListeners().onComponentTag(this, tag); // Call any tag handler onComponentTag(tag); // If we're an openclose tag if (!tag.isOpenClose() && !tag.isOpen()) { // We were something other than
or markupStream.throwMarkupException("Method renderComponent called on bad markup element: " + tag); } if (tag.isOpenClose() && openTag.isOpen()) { markupStream.throwMarkupException("You can not modify a open tag to open-close: " + tag); } try { // Render open tag boolean renderBodyOnly = getRenderBodyOnly(); if (renderBodyOnly) { ExceptionSettings.NotRenderableErrorStrategy notRenderableErrorStrategy = ExceptionSettings.NotRenderableErrorStrategy.LOG_WARNING; if (Application.exists()) { notRenderableErrorStrategy = getApplication().getExceptionSettings().getNotRenderableErrorStrategy(); } if (getFlag(FLAG_OUTPUT_MARKUP_ID)) { String message = String.format("Markup id set on a component that renders its body only. " + "Markup id: %s, component id: %s, type: %s, path: %s", getMarkupId(), getId(), getClass(), getPage().getPageClass() + ":" + getPageRelativePath()); if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) { throw new IllegalStateException(message); } log.warn(message); } if (getFlag(FLAG_PLACEHOLDER)) { String message = String.format("Placeholder tag set on a component that renders its body only. " + "Component id: %s, type: %s, path: %s\", ", getId(), getClass(), getPage().getPageClass() + ":" + getPageRelativePath()); if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) { throw new IllegalStateException(message); } log.warn(message); } } else { renderComponentTag(tag); } markupStream.next(); // Render the body only if open-body-close. Do not render if open-close. if (tag.isOpen()) { // Render the body. The default strategy will simply call the component's // onComponentTagBody() implementation. getMarkupSourcingStrategy().onComponentTagBody(this, markupStream, tag); // Render close tag if (openTag.isOpen()) { renderClosingComponentTag(markupStream, tag, renderBodyOnly); } else if (renderBodyOnly == false) { if (needToRenderTag(openTag)) { // Close the manually opened tag. And since the user might have changed the // tag name ... tag.writeSyntheticCloseTag(getResponse()); } } } } catch (WicketRuntimeException wre) { throw wre; } catch (RuntimeException re) { throw new WicketRuntimeException("Exception in rendering component: " + this, re); } } /** * * @param openTag * @return true, if the tag shall be rendered */ private boolean needToRenderTag(final ComponentTag openTag) { // If a open-close tag has been modified to be open-body-close then a // synthetic close tag must be rendered. boolean renderTag = (openTag != null && !(openTag instanceof WicketTag)); if (renderTag == false) { renderTag = !getApplication().getMarkupSettings().getStripWicketTags(); } return renderTag; } /** * Called to indicate that a component has been rendered. This method should only very rarely be * called at all. Some components may render its children without calling render() on them. * These components need to call rendered() to indicate that its child components were actually * rendered, the framework would think they had never been rendered, and in development mode * this would result in a runtime exception. */ public final void rendered() { Page page = findPage(); if (page != null) { // Tell the page that the component rendered page.componentRendered(this); } else { log.error("Component is not connected to a Page. Cannot register the component as being rendered. Component: " + toString()); } } /** * Get the markup sourcing strategy for the component. If null, * {@link #newMarkupSourcingStrategy()} will be called. * * @return Markup sourcing strategy */ protected final IMarkupSourcingStrategy getMarkupSourcingStrategy() { if (markupSourcingStrategy == null) { markupSourcingStrategy = newMarkupSourcingStrategy(); // If not strategy by provided, than we use a default one. if (markupSourcingStrategy == null) { markupSourcingStrategy = DefaultMarkupSourcingStrategy.get(); } } return markupSourcingStrategy; } /** * If {@link #getMarkupSourcingStrategy()} returns null, this method will be called. By default * it returns null, which means that a default markup strategy will be attached to the * 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() { return null; } /** * 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. *
* NOT intended for overriding by framework clients. Rather, use * {@link Component#renderHead(org.apache.wicket.markup.head.IHeaderResponse)} *
* * @param container * The HtmlHeaderContainer */ public void internalRenderHead(final HtmlHeaderContainer container) { if (isVisibleInHierarchy() && isRenderAllowed()) { if (log.isDebugEnabled()) { log.debug("internalRenderHead: {}", toString(false)); } IHeaderResponse response = container.getHeaderResponse(); // Allow component to contribute boolean wasRendered = response.wasRendered(this); if (wasRendered == false) { StringResponse markupHeaderResponse = new StringResponse(); Response oldResponse = getResponse(); RequestCycle.get().setResponse(markupHeaderResponse); try { // Make sure the markup source strategy contributes to the header first // to be backward compatible. WICKET-3761 getMarkupSourcingStrategy().renderHead(this, container); CharSequence headerContribution = markupHeaderResponse.getBuffer(); if (Strings.isEmpty(headerContribution) == false) { response.render(StringHeaderItem.forString(headerContribution)); } } finally { RequestCycle.get().setResponse(oldResponse); } // Then let the component itself to contribute to the header renderHead(response); } // Then ask all behaviors for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { Listpair = List.of(this, behavior); if (!response.wasRendered(pair)) { behavior.renderHead(this, response); response.markRendered(pair); } } } if (wasRendered == false) { response.markRendered(this); } } } /** * Replaces this component with another. The replacing component must have the same component id * as this component. This method serves as a shortcut to * * this.getParent().replace(replacement)
* * and provides a better context for errors. ** Usage:
* * @since 1.2.1 * * @param replacement * component to replace this one * @return the component which replaced this one */ public Component replaceWith(Component replacement) { Args.notNull(replacement, "replacement"); if (!getId().equals(replacement.getId())) { throw new IllegalArgumentException( "Replacement component must have the same id as the component it will replace. Replacement id [[" + replacement.getId() + "]], replaced id [[" + getId() + "]]."); } if (parent == null) { throw new IllegalStateException( "This method can only be called on a component that has already been added to its parent."); } parent.replace(replacement); return replacement; } /** * @param component * The component to compare with * @return True if the given component's model is the same as this component's model. */ public final boolean sameInnermostModel(final Component component) { return sameInnermostModel(component.getDefaultModel()); } /** * @param model * The model to compare with * @return True if the given component's model is the same as this component's model. */ public final boolean sameInnermostModel(final IModel> model) { // Get the two models IModel> thisModel = getDefaultModel(); // If both models are non-null they could be the same if (thisModel != null && model != null) { return getInnermostModel(thisModel) == getInnermostModel(model); } return false; } /** * Sets 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. 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 */ public final Component setEnabled(final boolean enabled) { // Is new enabled state a change? if (enabled != getFlag(FLAG_ENABLED)) { // Tell the page that this component's enabled was changed if (isVersioned()) { final Page page = findPage(); if (page != null) { addStateChange(); } } // Change visibility setFlag(FLAG_ENABLED, enabled); onEnabledStateChanged(); } return this; } void clearEnabledInHierarchyCache() { setRequestFlag(RFLAG_ENABLED_IN_HIERARCHY_SET, false); } void onEnabledStateChanged() { clearEnabledInHierarchyCache(); } /** * Sets whether model strings should be escaped. * * @param escapeMarkup * True is model strings should be escaped * @return This */ public final Component setEscapeModelStrings(final boolean escapeMarkup) { setFlag(FLAG_ESCAPE_MODEL_STRINGS, escapeMarkup); return this; } /** * Set markup ID, which must be String or Integer * * @param markupId */ public final void setMarkupIdImpl(Object markupId) { if (markupId != null && !(markupId instanceof String) && !(markupId instanceof Integer)) { throw new IllegalArgumentException("markupId must be String or Integer"); } setOutputMarkupId(true); if (markupId instanceof Integer) { generatedMarkupId = (Integer)markupId; setMetaData(MARKUP_ID_KEY, null); return; } generatedMarkupId = -1; setMetaData(MARKUP_ID_KEY, (String)markupId); } /** * Copy markupId * * @param comp */ final void setMarkupId(Component comp) { Args.notNull(comp, "comp"); generatedMarkupId = comp.generatedMarkupId; setMetaData(MARKUP_ID_KEY, comp.getMetaData(MARKUP_ID_KEY)); if (comp.getOutputMarkupId()) { setOutputMarkupId(true); } } /** * Sets this component's markup id to a user defined value. It is up to the user to ensure this * value is unique. *component = component.replaceWith(replacement);
** The recommended way is to let wicket generate the value automatically, this method is here to * serve as an override for that value in cases where a specific id must be used. *
* 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 */ public Component setMarkupId(String markupId) { Args.notEmpty(markupId, "markupId"); // TODO check if an automatic id has already been generated or getmarkupid() called // previously and throw an illegalstateexception because something else might be depending // on previous id setMarkupIdImpl(markupId); return this; } /** * 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
* The type of the metadata * * @param key * The singleton key for the metadata * @param object * The metadata object * @throws IllegalArgumentException * @see MetaDataKey */ @Override public final Component setMetaData(final MetaDataKey key, final M object) { MetaDataEntry>[] old = getMetaData(); Object metaData = null; MetaDataEntry>[] metaDataArray = key.set(old, object); if (metaDataArray != null && metaDataArray.length > 0) { metaData = (metaDataArray.length > 1) ? metaDataArray : metaDataArray[0]; } int index = getFlag(FLAG_MODEL_SET) ? 1 : 0; if (old == null && metaData != null) { data_insert(index, metaData); } else if (old != null && metaData != null) { data_set(index, metaData); } else if (old != null && metaData == null) { data_remove(index); } return this; } /** * Sets the given model. * * 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. *
* * @param model * The model * @return This */ public Component setDefaultModel(final IModel> model) { IModel> prevModel = getModelImpl(); IModel> wrappedModel = prevModel; if (prevModel instanceof IWrapModel) { wrappedModel = ((IWrapModel>)prevModel).getWrappedModel(); } // Change model if (wrappedModel != model) { // Detach the old/current model if (prevModel != null) { prevModel.detach(); } modelChanging(); setModelImpl(wrap(model)); modelChanged(); // WICKET-3413 reset 'inherited model' when model is explicitely set setFlag(FLAG_INHERITABLE_MODEL, false); } return this; } /** * @return model */ IModel> getModelImpl() { if (getFlag(FLAG_MODEL_SET)) { return (IModel>)data_get(0); } return null; } /** * * @param model */ void setModelImpl(IModel> model) { if (getFlag(FLAG_MODEL_SET)) { if (model != null) { data_set(0, model); } else { data_remove(0); setFlag(FLAG_MODEL_SET, false); } } else { if (model != null) { data_insert(0, model); setFlag(FLAG_MODEL_SET, true); } } } /** * Sets the backing model object. UnlikegetDefaultModel().setObject(object)
, this * method checks authorisation and model comparator, and invokesmodelChanging
and *modelChanged
if the value really changes. * * @param object * The object to set * @return This * @throws IllegalStateException If the component has neither its own model nor any of its * parents uses {@link IComponentInheritedModel} */ @SuppressWarnings("unchecked") public final Component setDefaultModelObject(final Object object) { final IModel* WARNING: be careful when overriding this method because it may open security holes - such as * allowing a user to click on a link that should be disabled. *
** Example usecase for overriding: Suppose you are building an component that displays images. * The component generates a callback to itself using {@link IRequestListener} interface and * uses this callback to stream image data. If such a component is placed inside a disabled * {@code WebMarkupContainer} we still want to allow the invocation of the request listener callback * method so that image data can be streamed. Such a component would override this method and * return {@literal true}. *
* * @return {@literal true} iff the listener method can be invoked on this component */ public boolean canCallListener() { return isEnabledInHierarchy() && isVisibleInHierarchy(); } /** * Render to the web response whatever the component wants to contribute to the head section. * * @param response * Response object */ @Override public void renderHead(IHeaderResponse response) { // noop } /** {@inheritDoc} */ @Override public void onEvent(IEvent> event) { } /** {@inheritDoc} */ @Override public finalvoid send(IEventSink sink, Broadcast type, T payload) { // if there are no event dispatchers then don't even try to send event if (getApplication().getFrameworkSettings().hasAnyEventDispatchers()) { new ComponentEventSender(this, getApplication().getFrameworkSettings()).send(sink, type, payload); } } /** * Removes behavior from component * * @param behaviors * behaviors to remove * * @return this (to allow method call chaining) */ public Component remove(final Behavior... behaviors) { for (Behavior behavior : behaviors) { Behaviors.remove(this, behavior); } return this; } /** {@inheritDoc} */ @Override public final Behavior getBehaviorById(int id) { return Behaviors.getBehaviorById(this, id); } /** {@inheritDoc} */ @Override public final int getBehaviorId(Behavior behavior) { if (behavior.isTemporary(this)) { throw new IllegalArgumentException( "Cannot get a stable id for temporary behavior " + behavior); } return Behaviors.getBehaviorId(this, behavior); } /** * Adds a behavior modifier to the component. * * @param behaviors * The behavior modifier(s) to be added * @return this (to allow method call chaining) */ public Component add(final Behavior... behaviors) { Behaviors.add(this, behaviors); return this; } /** * Gets the currently coupled {@link Behavior}s as an unmodifiable list. Returns an empty list * rather than null if there are no behaviors coupled to this component. * * @return The currently coupled behaviors as an unmodifiable list */ public final List extends Behavior> getBehaviors() { return getBehaviors(null); } @Override public boolean canCallListenerAfterExpiry() { return getApplication().getPageSettings() .getCallListenerAfterExpiry() || isStateless(); } /** * 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: * * *
* * 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.onReAdd(). */ protected void onReAdd() { setRequestFlag(RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED, true); } }- onInitialize is only called the very first time a component is added
*- onReAdd is not called the first time, but every time it is re-added after having been * removed
*