All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.Component Maven / Gradle / Ivy

Go to download

Wicket is a Java web application framework that takes simplicity, separation of concerns and ease of development to a whole new level. Wicket pages can be mocked up, previewed and later revised using standard WYSIWYG HTML design tools. Dynamic content processing and form handling is all handled in Java code using a first-class component model backed by POJO data beans that can easily be persisted using your favorite technology.

The newest version!
/*
 * 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.
 * 
 * 
    *
  • 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.
  • *
* * @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, 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: *

    *
  • 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.
  • *
*

*/ public static final Action RENDER = new Action(Action.RENDER); /** meta data for user specified markup id */ private static final MetaDataKey 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: *

    *
  • 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? *
* 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. *

*/ 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.
* 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. *

* Components like Panel and Border should return the "calling" markup fragment, e.g. * <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) { Optional delay = 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=\"\">"); } /** * 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)) { List pair = 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: component = component.replaceWith(replacement); *

* * @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. *

* 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. Unlike getDefaultModel().setObject(object), this * method checks authorisation and model comparator, and invokes modelChanging 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 model = (IModel)getDefaultModel(); // Check whether anything can be set at all if (model == null) { throw new IllegalStateException( "Attempt to set a model object on a component without a model! " + "Either pass an IModel to the constructor or use #setDefaultModel(new SomeModel(object)). " + "Component: " + getPageRelativePath()); } // Check authorization if (!isActionAuthorized(ENABLE)) { throw new UnauthorizedActionException(this, ENABLE); } // Check whether this will result in an actual change if (!getModelComparator().compare(this, object)) { modelChanging(); try { model.setObject(object); } catch (UnsupportedOperationException uox) { throw new WicketRuntimeException("You need to use writeable IModel for component " + getPageRelativePath(), uox); } modelChanged(); } return this; } /** * 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) { setFlag(FLAG_OUTPUT_MARKUP_ID, output); return this; } /** * Render a placeholder tag when the component is not visible. The tag is of form: * <componenttag hidden="" id="markupid"/>. This method will also call * setOutputMarkupId(true). * * 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 false the outputmarkupid flag is not * reverted to false. * * @param outputTag * @return this for chaining */ public final Component setOutputMarkupPlaceholderTag(final boolean outputTag) { if (outputTag != getFlag(FLAG_PLACEHOLDER)) { if (outputTag) { setOutputMarkupId(true); setFlag(FLAG_PLACEHOLDER, true); } else { setFlag(FLAG_PLACEHOLDER, false); // I think it's better to not setOutputMarkupId to false... // user can do it if she want } } return this; } /** * 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 */ public final Component setRenderBodyOnly(final boolean renderTag) { setFlag(FLAG_RENDER_BODY_ONLY, renderTag); return this; } /** * Sets the page that will respond to this request * * @param * * @param cls * The response page class * @see RequestCycle#setResponsePage(Class) */ public final void setResponsePage(final Class cls) { getRequestCycle().setResponsePage(cls, (PageParameters)null); } /** * Sets the page class and its parameters that will respond to this request * * @param * * @param cls * The response page class * @param parameters * The parameters for this bookmarkable page. * @see RequestCycle#setResponsePage(Class, PageParameters) */ public final void setResponsePage(final Class cls, PageParameters parameters) { getRequestCycle().setResponsePage(cls, parameters); } /** * 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 IRequestablePage page) { getRequestCycle().setResponsePage(page); } /** * @param versioned * True to turn on versioning for this component, false to turn it off for this * component and any children. * @return This */ public Component setVersioned(boolean versioned) { setFlag(FLAG_VERSIONED, versioned); return this; } /** * Sets whether this component and any children are visible. * * @param visible * True if this component and any children should be visible * @return This */ public final Component setVisible(final boolean visible) { // Is new visibility state a change? if (visible != getFlag(FLAG_VISIBLE)) { // record component's visibility change addStateChange(); // Change visibility setFlag(FLAG_VISIBLE, visible); onVisibleStateChanged(); } return this; } void clearVisibleInHierarchyCache() { setRequestFlag(RFLAG_VISIBLE_IN_HIERARCHY_SET, false); } void onVisibleStateChanged() { clearVisibleInHierarchyCache(); } /** * Gets the string representation of this component. * * @return The path to this component */ @Override public String toString() { return toString(false); } /** * @param detailed * True if a detailed string is desired * @return The string */ public String toString(final boolean detailed) { try { final StringBuilder buffer = new StringBuilder(); buffer.append("[Component id = ").append(getId()); if (detailed) { final Page page = findPage(); if (page == null) { buffer.append(", page = , path = ") .append(getPath()) .append('.') .append(Classes.simpleName(getClass())); } else { buffer.append(", page = ") .append(Classes.name(getPage().getPageClass())) .append(", path = ") .append(getPageRelativePath()) .append(", type = ") .append(Classes.name(getClass())) .append(", isVisible = ") .append((determineVisibility())) .append(", isVersioned = ") .append(isVersioned()); } if (markup != null) { buffer.append(", markup = ").append(new MarkupStream(getMarkup()).toString()); } } buffer.append(']'); return buffer.toString(); } catch (Exception e) { log.warn("Error while building toString()", e); return String.format( "[Component id = %s ]", getId(), e.getClass().getName()); } } /** * 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 * * @see RequestCycle#urlFor(Class, org.apache.wicket.request.mapper.parameter.PageParameters) * * @param pageClass * Class of page * @param parameters * Parameters to page * @return Bookmarkable URL to page */ public final CharSequence urlFor(final Class pageClass, final PageParameters parameters) { return getRequestCycle().urlFor(pageClass, parameters); } /** * Gets a URL for the listener interface on a behavior (e.g. {@link IRequestListener} on * {@link org.apache.wicket.ajax.markup.html.navigation.paging.AjaxPagingNavigationBehavior}). * * @param behaviour * The behavior that the URL should point to * @param parameters * The parameters that should be rendered into the urls * @return The URL */ public final CharSequence urlForListener(final Behavior behaviour, final PageParameters parameters) { int id = getBehaviorId(behaviour); IRequestHandler handler = createRequestHandler(parameters, id); return getRequestCycle().urlFor(handler); } /** * Create a suitable request handler depending whether the page is stateless or bookmarkable. */ private IRequestHandler createRequestHandler(PageParameters parameters, Integer id) { Page page = getPage(); PageAndComponentProvider provider = new PageAndComponentProvider(page, this, parameters); if (page.isPageStateless() || (page.isBookmarkable() && page.wasCreatedBookmarkable())) { return new BookmarkableListenerRequestHandler(provider, id); } else { return new ListenerRequestHandler(provider, id); } } /** * 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) { return getRequestCycle().urlFor(requestHandler); } /** * Gets a URL for this {@link IRequestListener}. * * @see RequestCycle#urlFor(IRequestHandler) * * @param parameters * The parameters that should be rendered into the URL * @return The URL */ public final CharSequence urlForListener(final PageParameters parameters) { IRequestHandler handler = createRequestHandler(parameters, null); return getRequestCycle().urlFor(handler); } /** * Returns a URL that references a shared resource through the provided resource reference. * * @see RequestCycle#urlFor(IRequestHandler) * * @param resourceReference * The resource reference * @param parameters * parameters or {@code null} if none * @return The url for the shared resource */ public final CharSequence urlFor(final ResourceReference resourceReference, PageParameters parameters) { return getRequestCycle().urlFor(resourceReference, parameters); } /** * Traverses all parent components of the given class in this parentClass, calling the visitor's * visit method at each one. * * @param * the type of the result object * @param parentClass * Class * @param visitor * The visitor to call at each parent of the given type * @return First non-null value returned by visitor callback */ public final R visitParents(final Class parentClass, final IVisitor visitor) { return visitParents(parentClass, visitor, IVisitFilter.ANY); } /** * Traverses all parent components of the given class in this parentClass, calling the visitor's * visit method at each one. * * @param * the type of the result object * @param parentClass * the class of the parent component * @param visitor * The visitor to call at each parent of the given type * @param filter * a filter that adds an additional logic to the condition whether a parent container * matches * @return First non-null value returned by visitor callback */ @SuppressWarnings("unchecked") public final R visitParents(final Class parentClass, final IVisitor visitor, IVisitFilter filter) { Args.notNull(filter, "filter"); // Start here MarkupContainer current = getParent(); Visit visit = new Visit(); // Walk up containment hierarchy while (current != null) { // Is current an instance of this class? if (parentClass.isInstance(current) && filter.visitObject(current)) { visitor.component((C)current, visit); if (visit.isStopped()) { return visit.getResult(); } } // Check parent current = current.getParent(); } return null; } /** * Registers a warning feedback message for this component. * * @param message * The feedback message */ @Override public final void warn(final Serializable message) { getFeedbackMessages().warn(this, message); addStateChange(); } /** * {@link Behavior#beforeRender(Component)} Notify all behaviors that are assigned to this * component that the component is about to be rendered. */ private void notifyBehaviorsComponentBeforeRender() { for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { behavior.beforeRender(this); } } } /** * {@link Behavior#afterRender(Component)} Notify all behaviors that are assigned to this * component that the component has rendered. */ private void notifyBehaviorsComponentRendered() { // notify the behaviors that component has been rendered for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { behavior.afterRender(this); } } } /** * 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() { checkHierarchyChange(this); final Page page = findPage(); if (page != null) { page.componentStateChanging(this); } } /** * Checks whether the given type has the expected name. * * @param tag * The tag to check * @param name * The expected tag name * @throws MarkupException * Thrown if the tag is not of the right name */ protected final void checkComponentTag(final ComponentTag tag, final String name) { if (!tag.getName().equalsIgnoreCase(name)) { String msg = String.format("Component [%s] (path = [%s]) must be " + "applied to a tag of type [%s], not: %s", getId(), getPath(), name, tag.toUserDebugString()); findMarkupStream().throwMarkupException(msg); } } /** * Checks that a given tag has a required attribute value. * * @param tag * The tag * @param key * The attribute key * @param values * The required value for the attribute key * @throws MarkupException * Thrown if the tag does not have the required attribute value */ protected final void checkComponentTagAttribute(final ComponentTag tag, final String key, final String... values) { if (key != null) { final String tagAttributeValue = tag.getAttributes().getString(key); boolean found = false; if (tagAttributeValue != null) { for (String value : values) { if (value.equalsIgnoreCase(tagAttributeValue)) { found = true; break; } } } if (found == false) { String msg = String.format("Component [%s] (path = [%s]) must be applied to a tag " + "with [%s] attribute matching any of %s, not [%s]", getId(), getPath(), key, Arrays.toString(values), tagAttributeValue); findMarkupStream().throwMarkupException(msg); } } } /** * 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 */ protected void checkHierarchyChange(final Component component) { // Throw exception if modification is attempted during rendering if (getRequestFlag(RFLAG_RENDERING) && !component.isAuto()) { throw new WicketRuntimeException( "Cannot modify component hierarchy after render phase has started (page version cant change then anymore)"); } } /** * Detaches the model for this component if it is detachable. */ protected void detachModel() { IModel model = getModelImpl(); if (model != null) { model.detach(); } // also detach the wrapped model of a component assigned wrap (not // inherited) if (model instanceof IWrapModel && !getFlag(FLAG_INHERITABLE_MODEL)) { ((IWrapModel)model).getWrappedModel().detach(); } } /** * Suffixes an exception message with useful information about this. component. * * @param message * The message * @return The modified message */ protected final String exceptionMessage(final String message) { return message + ":\n" + toString(); } /** * 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. */ protected final MarkupStream findMarkupStream() { return new MarkupStream(getMarkup()); } /** * 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, {@code null} is returned. * * @return The Page or {@code null} if none can be found */ protected final Page findPage() { // Search for page return (Page)(this instanceof Page ? this : findParent(Page.class)); } /** * 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 * unmodifiable list * @param * A class derived from Behavior */ public List getBehaviors(Class type) { return Behaviors.getBehaviors(this, type); } /** * 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 */ protected final boolean getFlag(final int flag) { return (flags & flag) != 0; } /** * 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 */ protected final boolean getRequestFlag(final short flag) { return (requestFlags & flag) != 0; } /** * Finds the innermost IModel object for an IModel that might contain nested IModel(s). * * @param model * The model * @return The innermost (most nested) model */ protected final IModel getInnermostModel(final IModel model) { IModel nested = model; while (nested != null && nested instanceof IWrapModel) { final IModel next = ((IWrapModel)nested).getWrappedModel(); if (nested == next) { throw new WicketRuntimeException("Model for " + nested + " is self-referential"); } nested = next; } return nested; } /** * 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() { return defaultModelComparator; } /** * Returns whether the component can be stateless. Also the component behaviors must be * 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() { return true; } /** * Called when a null model is about to be retrieved in order to allow a subclass to provide an * initial model. *

* By default this implementation looks components in the parent chain owning a * {@link IComponentInheritedModel} to provide a model for this component via * {@link IComponentInheritedModel#wrapOnInheritance(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() { IModel foundModel = null; // Search parents for IComponentInheritedModel (i.e. CompoundPropertyModel) for (Component current = getParent(); current != null; current = current.getParent()) { // Get model // Don't call the getModel() that could initialize many in between // completely useless models. // IModel model = current.getDefaultModel(); IModel model = current.getModelImpl(); if (model instanceof IWrapModel && !(model instanceof IComponentInheritedModel)) { model = ((IWrapModel)model).getWrappedModel(); } if (model instanceof IComponentInheritedModel) { // return the shared inherited foundModel = ((IComponentInheritedModel)model).wrapOnInheritance(this); setFlag(FLAG_INHERITABLE_MODEL, true); break; } } // No model for this component! return foundModel; } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL OR OVERRIDE. * *

* Called anytime a model is changed via setModel or setModelObject. *

*/ protected void internalOnModelChanged() { } /** * Components are allowed to reject behavior modifiers. * * @param behavior * @return False, if the component should not apply this behavior */ protected boolean isBehaviorAccepted(final Behavior behavior) { // Ignore AttributeModifiers when FLAG_IGNORE_ATTRIBUTE_MODIFIER is set if ((behavior instanceof AttributeModifier) && (getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER) != false)) { return false; } return behavior.isEnabled(this); } /** * If true, all attribute modifiers will be ignored * * @return True, if attribute modifiers are to be ignored */ protected final boolean isIgnoreAttributeModifier() { return getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER); } /** * Called immediately after a component and all its children have been rendered, * regardless of any exception. */ protected void onAfterRender() { setRequestFlag(RFLAG_AFTER_RENDER_SUPER_CALL_VERIFIED, true); } /** * Called on all visible components before any component is rendered. *

* NOTE: 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. *

* * Changes to the component tree can be made only before calling * super.onBeforeRender(). * * @see org.apache.wicket.MarkupContainer#addOrReplace(Component...) */ protected void onBeforeRender() { setRequestFlag(RFLAG_PREPARED_FOR_RENDER, true); onBeforeRenderChildren(); setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, true); } /** * Processes the component tag. * * Overrides of this method most likely should call the super implementation. * * @param tag * Tag to modify */ protected void onComponentTag(final ComponentTag tag) { // We can't try to get the ID from markup. This could be different than // id returned from getMarkupId() prior first rendering the component // (due to transparent resolvers and borders which break the 1:1 // component <-> markup relation) if (getFlag(FLAG_OUTPUT_MARKUP_ID)) { tag.putInternal(MARKUP_ID_ATTR_NAME, getMarkupId()); } DebugSettings debugSettings = getApplication().getDebugSettings(); String componentPathAttributeName = debugSettings.getComponentPathAttributeName(); if (Strings.isEmpty(componentPathAttributeName) == false) { String path = getPageRelativePath(); path = path.replace("_", "__"); path = path.replace(':', '_'); tag.put(componentPathAttributeName, path); } // The markup sourcing strategy may also want to work on the tag getMarkupSourcingStrategy().onComponentTag(this, tag); } /** * Processes the body. * * @param markupStream * The markup stream * @param openTag * The open tag for the body */ public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) { } /** * 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. */ protected void onDetach() { setRequestFlag(RFLAG_DETACHING, false); } /** * 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. */ protected void onRemove() { setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, false); } /** * Called anytime a model is changed after the change has occurred */ protected void onModelChanged() { } /** * Called anytime a model is changed, but before the change actually occurs */ protected void onModelChanging() { } /** * Implementation that renders this component. */ protected abstract void onRender(); /** * 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 */ protected final void renderComponentTag(ComponentTag tag) { if (needToRenderTag(tag)) { // apply behaviors that are attached to the component tag. if (tag.hasBehaviors()) { tag = tag.mutable(); Iterator tagBehaviors = tag.getBehaviors(); while (tagBehaviors.hasNext()) { final Behavior behavior = tagBehaviors.next(); if (behavior.isEnabled(this)) { behavior.onComponentTag(this, tag); } behavior.detach(this); } } // Apply behavior modifiers List behaviors = getBehaviors(); if ((behaviors != null) && !behaviors.isEmpty() && !tag.isClose() && (isIgnoreAttributeModifier() == false)) { tag = tag.mutable(); for (Behavior behavior : behaviors) { // Components may reject some behavior components if (isBehaviorAccepted(behavior)) { behavior.onComponentTag(this, tag); } } } if ((tag instanceof WicketTag) && !tag.isClose() && !getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER)) { ExceptionSettings.NotRenderableErrorStrategy notRenderableErrorStrategy = ExceptionSettings.NotRenderableErrorStrategy.LOG_WARNING; if (Application.exists()) { notRenderableErrorStrategy = getApplication().getExceptionSettings().getNotRenderableErrorStrategy(); } String tagName = tag.getNamespace() + ":" + tag.getName(); String componentId = getId(); if (getFlag(FLAG_OUTPUT_MARKUP_ID)) { String message = String.format("Markup id set on a component that is usually not rendered into markup. " + "Markup id: %s, component id: %s, component tag: %s.", getMarkupId(), componentId, tagName); 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 is usually not rendered into markup. " + "Component id: %s, component tag: %s.", componentId, tagName); if (notRenderableErrorStrategy == ExceptionSettings.NotRenderableErrorStrategy.THROW_EXCEPTION) { throw new IllegalStateException(message); } log.warn(message); } } // Write the tag tag.writeOutput(getResponse(), !needToRenderTag(null), getMarkup().getMarkupResourceStream().getWicketNamespace()); } } /** * Replaces the body with the given one. * * @param markupStream * The markup stream to replace the tag body in * @param tag * The tag * @param body * The new markup */ protected final void replaceComponentTagBody(final MarkupStream markupStream, final ComponentTag tag, final CharSequence body) { // The tag might have been changed from open-close to open. Hence // we'll need what was in the markup itself ComponentTag markupOpenTag = null; // If tag has a body if (tag.isOpen()) { // Get what tag was in the markup; not what the user it might // have changed it to. markupOpenTag = markupStream.getPreviousTag(); // If it was an open tag in the markup as well, than ... if (markupOpenTag.isOpen()) { // skip any raw markup in the body markupStream.skipRawMarkup(); } } if (body != null) { // Write the new body getResponse().write(body); } // If we had an open tag (and not an openclose tag) and we found a // close tag, we're good if (tag.isOpen()) { // If it was an open tag in the markup, than there must be // a close tag as well. if ((markupOpenTag != null) && markupOpenTag.isOpen() && !markupStream.atCloseTag()) { // There must be a component in this discarded body markupStream.throwMarkupException("Expected close tag for '" + markupOpenTag + "' Possible attempt to embed component(s) '" + markupStream.get() + "' in the body of this component which discards its body"); } } } /** * @param auto * True to put component into auto-add mode */ protected final Component setAuto(final boolean auto) { setFlag(FLAG_AUTO, auto); return this; } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * @param flag * The flag to set * @param set * True to turn the flag on, false to turn it off */ protected final Component setFlag(final int flag, final boolean set) { if (set) { flags |= flag; } else { flags &= ~flag; } return this; } /** * @param flag * The flag to set * @param set * True to turn the flag on, false to turn it off */ final Component setRequestFlag(final short flag, final boolean set) { if (set) { requestFlags |= flag; } else { requestFlags &= ~flag; } return this; } /** * If true, all attribute modifiers will be ignored * * @param ignore * If true, all attribute modifiers will be ignored * @return This */ protected final Component setIgnoreAttributeModifier(final boolean ignore) { setFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER, ignore); return this; } /** * @param * The model type * @param model * The model to wrap if need be * @return The wrapped model */ protected final IModel wrap(final IModel model) { if (model instanceof IComponentAssignedModel) { return ((IComponentAssignedModel)model).wrapOnAssignment(this); } return model; } /** * Detaches any child components */ void detachChildren() { } /** * Signals this components removal from hierarchy to all its children. */ void removeChildren() { } /** * Gets the component at the given path. * * @param path * Path to component * @return The component at the path */ @Override public Component get(final String path) { // Path to this component is an empty path if (path.length() == 0) { return this; } throw new IllegalArgumentException( exceptionMessage("Component is not a container and so does not contain the path " + path)); } /** * @param setRenderingFlag * rendering flag */ void internalMarkRendering(boolean setRenderingFlag) { // WICKET-5460 no longer prepared for render setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false); setRequestFlag(RFLAG_RENDERING, setRenderingFlag); } /** * @return True if this component or any of its parents is in auto-add mode */ public final boolean isAuto() { // Search up hierarchy for FLAG_AUTO for (Component current = this; current != null; current = current.getParent()) { if (current.getFlag(FLAG_AUTO)) { return true; } } return false; } /** * * @return true if component has been prepared for render */ boolean isPreparedForRender() { return getRequestFlag(RFLAG_PREPARED_FOR_RENDER); } /** * This method is here for {@link MarkupContainer}. It is broken out of * {@link #onBeforeRender()} so we can guarantee that it executes as the last in * onBeforeRender() chain no matter where user places the super.onBeforeRender() * call. */ void onBeforeRenderChildren() { } /** * Renders the close tag at the current position in the markup stream. * * @param markupStream * the markup stream * @param openTag * the tag to render * @param renderBodyOnly * if true, the tag will not be written to the output */ final void renderClosingComponentTag(final MarkupStream markupStream, final ComponentTag openTag, final boolean renderBodyOnly) { // Tag should be open tag and not openclose tag if (openTag.isOpen()) { // If we found a close tag and it closes the open tag, we're good if (markupStream.atCloseTag() && markupStream.getTag().closes(openTag)) { // Render the close tag if ((renderBodyOnly == false) && needToRenderTag(openTag)) { openTag.writeSyntheticCloseTag(getResponse()); } } else if (openTag.requiresCloseTag()) { // Missing close tag. Some tags, e.g.

are handled like

by most browsers and // thus will not throw an exception. markupStream.throwMarkupException("Expected close tag for " + openTag); } } } /** * Sets the id of this component. * * @param id * The non-null id of this component */ private void checkId(final String id) { if (!(this instanceof Page)) { if (Strings.isEmpty(id)) { throw new WicketRuntimeException("Null or empty component ID's are not allowed."); } } if ((id != null) && (id.indexOf(':') != -1 || id.indexOf('~') != -1)) { throw new WicketRuntimeException("The component ID must not contain ':' or '~' chars."); } } /** * 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). *

* 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 */ public final void setParent(final MarkupContainer parent) { if (this.parent != null && log.isDebugEnabled()) { log.debug("Replacing parent " + this.parent + " with " + parent); } this.parent = parent; } /** * Sets the render allowed flag. * * @param renderAllowed */ final void setRenderAllowed(boolean renderAllowed) { setFlag(FLAG_IS_RENDER_ALLOWED, renderAllowed); } /** * 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 * of this page */ void setRenderAllowed() { setRenderAllowed(isActionAuthorized(RENDER)); } /** * Sets whether or not this component is allowed to be visible. This method is meant to be used * by components to control visibility of other components. A call to * {@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 final so their contract is enforced always. * * @param allowed * @return this for chaining */ public final Component setVisibilityAllowed(boolean allowed) { if (allowed != getFlag(FLAG_VISIBILITY_ALLOWED)) { setFlag(FLAG_VISIBILITY_ALLOWED, allowed); onVisibleStateChanged(); } return this; } /** * 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() { return getFlag(FLAG_VISIBILITY_ALLOWED); } /** * Determines whether or not a component should be visible, taking into account all the factors: * {@link #isVisible()}, {@link #isVisibilityAllowed()}, {@link #isRenderAllowed()} * * @return true if the component should be visible, false otherwise */ public final boolean determineVisibility() { return isVisible() && isRenderAllowed() && isVisibilityAllowed(); } /** * 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 true), and all of its parents are enabled. * * @return true if this component is enabled */ 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. *

* 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 final void 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 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: * *
    *
  • 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
  • *
* * 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); } }