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

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

Go to download

Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and unloading of Wicket components and pageSources.

There is a newer version: 5.0.0
Show 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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.apache.wicket.ajax.IAjaxRegionMarkupIdProvider;
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.behavior.Behavior;
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.FeedbackMessage;
import org.apache.wicket.feedback.IFeedback;
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.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
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.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.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.handler.BookmarkableListenerInterfaceRequestHandler;
import org.apache.wicket.request.handler.ListenerInterfaceRequestHandler;
import org.apache.wicket.request.handler.PageAndComponentProvider;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.settings.IDebugSettings;
import org.apache.wicket.util.IHierarchical;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.lang.WicketObjects;
import org.apache.wicket.util.string.ComponentStrings;
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.IVisit;
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 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. * *
  • Component Paths - 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 * getPath(). To get a Component path relative to the page that contains it, you can call * 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. * *
    • Request Handling - An incoming request is processed by a protocol request handler such * as WicketServlet. An associated Application object creates Session, Request and 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 a listener on an existing Component, * that listener is called, 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. For * example, the {@link org.apache.wicket.markup.html.form.IFormSubmitListener#onFormSubmitted()} * method implemented by the Form class is really a private implementation detail of the Form class * that is not designed to be overridden (although unfortunately, it must be public since all * interface methods in Java must be public). Instead, Form subclasses should override user-oriented * methods such as onValidate(), onSubmit() and onError() (although only the latter two are likely * to be overridden in practice). * *
    • Form Submit - If a Form has been submitted and the Component is a FormComponent, the * component's model is validated by a call to FormComponent.validate(). * *
    • Form Model Update - If a valid Form has been submitted and the Component is a * FormComponent, the component's model is updated by a call to FormComponent.updateModel(). * *
    • Rendering - A markup response is generated by the Component via * {@link Component#render()}, which calls subclass implementation code contained in * {@link org.apache.wicket.Component#onRender()}. Once this phase begins, a Component becomes * immutable. Attempts to alter the Component will result in a WicketRuntimeException. * *
    * *
  • Component Models - The primary responsibility of a component is to use its model (an * object that implements IModel), which can be set via * {@link Component#setDefaultModel(IModel model)} and retrieved via * {@link Component#getDefaultModel()}, to render a response in an appropriate markup language, such * as HTML. In addition, form components know how to update their models based on request * information. Since the IModel interface is a wrapper around an actual model object, a convenience * method {@link Component#getDefaultModelObject()} is provided to retrieve the model Object from * its IModel wrapper. A further convenience method, * {@link Component#getDefaultModelObjectAsString()} , is provided for the very common operation of * converting the wrapped model Object to a String. * *
  • Visibility - Components which have setVisible(false) will return false from * isVisible() and will not render a response (nor will their children). * *
  • 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. * *
  • Session - The Page for a Component points back to the Session that contains the Page. * The Session for a component can be accessed with the convenience method getSession(), which * simply calls getPage().getSession(). * *
  • Locale - The Locale for a Component is available through the convenience method * getLocale(), which is equivalent to getSession().getLocale(). * *
  • 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. * *
  • Style - The style ("skin") for a component is available through * {@link org.apache.wicket.Component#getStyle()}, which is equivalent to getSession().getStyle(). * Styles are intended to give a particular look to a Component or Resource 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 the Variation is "NorthSea", than the resources are * given the names suffixed with "_ocean_NorthSea". * *
  • AttributeModifiers - You can add one or more {@link AttributeModifier}s to any * component if you need to programmatically manipulate attributes of the markup tag to which a * Component is attached. * *
  • Application, ApplicationSettings and ApplicationPages - The getApplication() method * provides convenient access to the Application for a Component via getSession().getApplication(). * The getApplicationSettings() method is equivalent to getApplication().getSettings(). The * getApplicationPages is equivalent to getApplication().getPages(). * *
  • 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()}. * *
  • AJAX support- Components can be re-rendered after the whole Page has been rendered at * least once by calling doRender(). * * @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 { /** 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; }; /** Basic model IModelComparator implementation for normal object models */ private static final IModelComparator defaultModelComparator = new IModelComparator() { private static final long serialVersionUID = 1L; 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); } }; /** an unused flag */ private static final int FLAG_UNUSED0 = 0x20000000; private static final int FLAG_UNUSED1 = 0x800000; private static final int FLAG_UNUSED2 = 0x1000000; private static final int FLAG_UNUSED3 = 0x10000000; /** 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; /** Reserved subclass-definable flag bit */ private static final int FLAG_NOTUSED7 = 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; /** True when a component is being removed from the hierarchy */ protected static final int FLAG_REMOVING_FROM_HIERARCHY = 0x200000; /** * 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 int FLAG_RENDERING = 0x2000000; private static final int FLAG_PREPARED_FOR_RENDER = 0x4000000; private static final int FLAG_AFTER_RENDERING = 0x8000000; private static final int FLAG_MARKUP_ATTACHED = 0x10000000; /** * 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; private static final int FLAG_DETACHING = 0x80000000; /** * 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; 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_HIEARARCHY_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; /** * 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 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) { setId(id); getApplication().getComponentInstantiationListeners().onInstantiation(this); final IDebugSettings debugSettings = getApplication().getDebugSettings(); if (debugSettings.isLinePreciseReportingOnNewComponentEnabled()) { setMetaData(CONSTRUCTED_AT_KEY, ComponentStrings.toString(this, new MarkupException("constructed"))); } if (model != null) { setModelImpl(wrap(model)); } } /** * 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; } /** * Called when the component gets added to a parent * * @return false, if it was called the first time */ private boolean internalOnMarkupAttached() { boolean rtn = getFlag(FLAG_MARKUP_ATTACHED); if (rtn == false) { setFlag(FLAG_MARKUP_ATTACHED, true); onMarkupAttached(); } return rtn; } /** * Can be subclassed by any user to implement init-like logic which requires the markup to be * available * * @deprecated Use #onInitialize() instead. */ @Deprecated protected void onMarkupAttached() { if (log.isDebugEnabled()) { log.debug("Markup available " + toString()); } } /** * @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-component are using it. You may also * it if you subclassed getMarkup(). * * @param markup */ public final void setMarkup(final IMarkupFragment markup) { this.markup = markup; } /** * Called once per request on components before they are about to be 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 visibility of a * component is toggled inside {@link #onBeforeRender()} another method needs to be overridden * to make sure {@link #onBeforeRender()} will be invoked on invisible components: * *

    	 * class MyComponent extends WebComponent
    	 * {
    	 * 	protected void onBeforeRender()
    	 * 	{
    	 * 		setVisible(Math.rand() > 0.5f);
    	 * 		super.onBeforeRender();
    	 * 	}
    	 * 
    	 * 	// if this override is forgotten, once invisible component will never become visible
    	 * 	protected boolean callOnBeforeRenderIfNotVisible()
    	 * 	{
    	 * 		return true;
    	 * 	}
    	 * }
    	 * 
    * * VS * *
    	 * class MyComponent extends WebComponent
    	 * {
    	 * 	protected void onConfigure()
    	 * 	{
    	 * 		super.onConfigure();
    	 * 		setVisible(Math.rand() > 0.5f);
    	 * 	}
    	 * }
    	 * 
    */ protected void onConfigure() { } /** * 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); try { internalOnMarkupAttached(); } catch (WicketRuntimeException exception) { // ignore } } /** * Checks if the component has been initialized - {@link #onInitialize()} has been called * * @return {@code true} if component has been initialized */ final boolean isInitialized() { return getFlag(FLAG_INITIALIZED); } /** * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL 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(); if (!getRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED)) { throw new IllegalStateException(Component.class.getName() + " has not been properly initialized. Something in the hierarchy of " + getClass().getName() + " has not called super.onInitialize() in the override of onInitialize() method"); } setRequestFlag(RFLAG_INITIALIZE_SUPER_CALL_VERIFIED, false); getApplication().getComponentInitializationListeners().onInitialize(this); } } /** * Called on very component after the page is rendered. It will call onAfterRender for it self * and its children. */ public final void afterRender() { // if the component has been previously attached via attach() // detach it now try { setFlag(FLAG_AFTER_RENDERING, true); // always detach children because components can be attached // independently of their parents onAfterRenderChildren(); onAfterRender(); getApplication().getComponentOnAfterRenderListeners().onAfterRender(this); if (getFlag(FLAG_AFTER_RENDERING)) { throw new IllegalStateException(Component.class.getName() + " has not been properly detached. Something in the hierarchy of " + getClass().getName() + " has not called super.onAfterRender() in the override of onAfterRender() method"); } } finally { // this flag must always be set to false. setFlag(FLAG_RENDERING, false); } } /** * */ private final void internalBeforeRender() { configure(); if ((determineVisibility()) && !getFlag(FLAG_RENDERING) && !getFlag(FLAG_PREPARED_FOR_RENDER)) { setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, false); getApplication().getComponentPreOnBeforeRenderListeners().onBeforeRender(this); onBeforeRender(); getApplication().getComponentPostOnBeforeRenderListeners().onBeforeRender(this); if (!getRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED)) { throw new IllegalStateException(Component.class.getName() + " has not been properly rendered. Something in the hierarchy of " + getClass().getName() + " has not called super.onBeforeRender() in the override of onBeforeRender() method"); } } } /** * We need to postpone calling beforeRender() on components that implement {@link IFeedback}, to * be sure that all other component's beforeRender() has been already called, so that IFeedbacks * can collect all feedback messages. This is the key under list of postponed {@link IFeedback} * is stored to request cycle metadata. The List is then iterated over in * {@link #prepareForRender()} after calling {@link #beforeRender()}, to initialize postponed * components. */ private static final MetaDataKey> FEEDBACK_LIST = new MetaDataKey>() { private static final long serialVersionUID = 1L; }; /** * Called for every component when the page is getting to be rendered. it will call * onBeforeRender for this component and all the child components */ public final void beforeRender() { if (!(this instanceof IFeedback)) { internalBeforeRender(); } else { // this component is a feedback. Feedback must be initialized last, so that // they can collect messages from other components List feedbacks = getRequestCycle().getMetaData(FEEDBACK_LIST); if (feedbacks == null) { feedbacks = new ArrayList(); getRequestCycle().setMetaData(FEEDBACK_LIST, feedbacks); } if (this instanceof MarkupContainer) { ((MarkupContainer)this).visitChildren(IFeedback.class, new IVisitor() { public void component(Component component, IVisit visit) { component.beforeRender(); } }); } if (!feedbacks.contains(this)) { feedbacks.add(this); } } } /** * 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(); onConfigure(); for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { behavior.onConfigure(this); } } // check authorization setRenderAllowed(); internalOnAfterConfigure(); setRequestFlag(RFLAG_CONFIGURED, true); } } /** * * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Called after the {@link #onConfigure()}, but before {@link #onBeforeRender()} */ void internalOnAfterConfigure() { } /** * Redirects to any intercept page previously specified by a call to redirectToInterceptPage. * * @return True if an original destination was redirected to * @see Component#redirectToInterceptPage(Page) */ public final boolean continueToOriginalDestination() { return RestartResponseAtInterceptPageException.continueToOriginalDestination(); } /** * Registers a debug feedback message for this component * * @param message * The feedback message */ public final void debug(final Serializable message) { getSession().getFeedbackMessages().debug(this, message); getSession().dirty(); } /** * Signals this Component that it is removed from the Component hierarchy. */ final void internalOnRemove() { setFlag(FLAG_REMOVING_FROM_HIERARCHY, true); onRemove(); if (getFlag(FLAG_REMOVING_FROM_HIERARCHY)) { throw new IllegalStateException(Component.class.getName() + " has not been properly removed from hierachy. Something in the hierarchy of " + getClass().getName() + " has not called super.onRemovalFromHierarchy() in the override of onRemovalFromHierarchy() method"); } removeChildren(); } /** * Detaches the component. This is called at the end of the request for all the pages that are * touched in that request. */ public final void detach() { // if the component has been previously attached via attach() // detach it now setFlag(FLAG_DETACHING, true); onDetach(); if (getFlag(FLAG_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 new Behaviors(this).detach(); // 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(); requestFlags = 0; internalDetach(); // notify any detach listener IDetachListener detachListener = getApplication().getFrameworkSettings() .getDetachListener(); if (detachListener != null) { detachListener.onDetach(this); } } /** * 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 */ public final void error(final Serializable message) { getSession().getFeedbackMessages().error(this, message); getSession().dirty(); } /** * Registers an fatal error feedback message for this component * * @param message * The feedback message */ public final void fatal(final Serializable message) { getSession().getFeedbackMessages().fatal(this, message); getSession().dirty(); } /** * 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(); } /** * Gets the converter that should be used by this component. * * @param type * The type to convert to * * @return The converter that should be used by this component */ public IConverter getConverter(Class type) { return getApplication().getConverterLocator().getConverter(type); } /** * 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); } /** * @return Any feedback message for this component */ @SuppressWarnings("deprecation") public final FeedbackMessage getFeedbackMessage() { return getSession().getFeedbackMessages().messageForComponent(this); } /** * @return All feedback messages for this component */ public final List getFeedbackMessages() { return getSession().getFeedbackMessages().messagesForComponent(this); } /** * Gets the id of this component. * * @return The id of this component */ 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(); } /** * Get the first component tag in the associated markup * * @return first component tag */ private 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 IS WICKET INTERNAL ONLY. 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; } /** * Find the Page and get net value from an auto-index * * @return autoIndex */ private final int nextAutoIndex() { Page page = findPage(); if (page == null) { throw new WicketRuntimeException( "This component is not (yet) coupled to a page. It has to be able " + "to find the page it is supposed to operate in before you can call " + "this method (Component#getMarkupId)"); } return page.getAutoIndex(); } /** * 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. *

    * *

    * 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) { Object storedMarkupId = getMarkupIdImpl(); if (storedMarkupId instanceof String) { return (String)storedMarkupId; } if (storedMarkupId == null && createIfDoesNotExist == false) { return null; } int generatedMarkupId = storedMarkupId instanceof Integer ? (Integer)storedMarkupId : getSession().nextSequenceValue(); if (generatedMarkupId == 0xAD) { // WICKET-4559 skip suffix 'ad' because some ad-blocking solutions may hide the component generatedMarkupId = getSession().nextSequenceValue(); } if (storedMarkupId == null) { setMarkupIdImpl(generatedMarkupId); } String markupIdPrefix = "id"; if (!getApplication().usesDeploymentConfig()) { // in non-deployment mode we make the markup id include component id // so it is easier to debug markupIdPrefix = getId(); } String markupIdPostfix = Integer.toHexString(generatedMarkupId).toLowerCase(); String markupId = markupIdPrefix + markupIdPostfix; // make sure id is compliant with w3c requirements (starts with a letter) char c = markupId.charAt(0); if (!Character.isLetter(c)) { markupId = "id" + markupId; } // escape some noncompliant characters markupId = Strings.replaceAll(markupId, "_", "__").toString(); markupId = markupId.replace('.', '_'); markupId = markupId.replace('-', '_'); markupId = markupId.replace(' ', '_'); 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. *

    * 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 */ public final M getMetaData(final MetaDataKey key) { return key.get(getMetaData()); } /** * * @return meta data entry */ 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 RuntimeException rex = new RuntimeException("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 IllegalStateException * Thrown if component is not yet attached to a Page. */ 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); } return page; } /** * Gets the path to this component relative to the page it is in. * * @return The path to this component relative to the page it is in */ 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 */ 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 */ public long getSizeInBytes() { final MarkupContainer originalParent = parent; parent = null; long size = -1; 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 convinient method. Same as Session.get().getStyle(). * * @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); } /** * @return True if this component has an error message */ public final boolean hasErrorMessage() { return getSession().getFeedbackMessages().hasErrorMessageFor(this); } /** * @return True if this component has some kind of feedback message */ public final boolean hasFeedbackMessage() { return getSession().getFeedbackMessages().hasMessageFor(this); } /** * Registers an informational feedback message for this component * * @param message * The feedback message */ public final void info(final Serializable message) { getSession().getFeedbackMessages().info(this, message); getSession().dirty(); } /** * Registers an success feedback message for this component * * @param message * The feedback message */ public final void success(final Serializable message) { getSession().getFeedbackMessages().success(this, message); getSession().dirty(); } /** * 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 ture 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 (!getStatelessHint()) { return false; } if ( // the component is either invisible or disabled (isVisibleInHierarchy() && isEnabledInHierarchy()) == false && // and it can't call listener interfaces canCallListenerInterface(null) == false ) { // then pretend the component is stateless return true; } 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() { Component parent = getParent(); if (parent != null && !parent.isVisibleInHierarchy()) { return false; } else { return determineVisibility(); } } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Sets the RENDERING flag and removes the PREPARED_FOR_RENDER flag on component and it's * children. * * @param setRenderingFlag * if this is false only the PREPARED_FOR_RENDER flag is removed from component, the * RENDERING flag is not set. * * @see #internalPrepareForRender(boolean) */ public final void markRendering(boolean setRenderingFlag) { 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); } } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! *

    * Prepares the component and it's children for rendering. On whole page render this method must * be called on the page. On AJAX request, this method must be called on the updated component. * * @param setRenderingFlag * Whether to set the rendering flag. This must be true if the page is about to be * rendered. However, there are usecases to call this method without an immediate * render (e.g. on stateless listener request target to build the component * hierarchy), in that case setRenderingFlag should be false. */ public void internalPrepareForRender(boolean setRenderingFlag) { beforeRender(); if (setRenderingFlag) { // only process feedback panel when we are about to be rendered. // setRenderingFlag is false in case prepareForRender is called only to build component // hierarchy (i.e. in BookmarkableListenerInterfaceRequestTarget). // prepareForRender(true) is always called before the actual rendering is done so // that's where feedback panels gather the messages List feedbacks = getRequestCycle().getMetaData(FEEDBACK_LIST); if (feedbacks != null) { for (Component feedback : feedbacks) { feedback.internalBeforeRender(); } } getRequestCycle().setMetaData(FEEDBACK_LIST, null); } markRendering(setRenderingFlag); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Prepares the component and it's children for rendering. On whole page render this method must * be called on the page. On AJAX request, this method must be called on updated component. */ public final void prepareForRender() { internalPrepareForRender(true); } /** * Redirects browser to an intermediate page such as a sign-in page. The current request's url * is saved for future use by method continueToOriginalDestination(); Only use this method when * you plan to continue to the current url at some later time; otherwise just use * setResponsePage or - when you are in a constructor or checkAccessMethod, call redirectTo. * * @param page * The sign in page * * @see Component#continueToOriginalDestination() */ public final void redirectToInterceptPage(final Page page) { 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); } /** * Render the Component. */ public final void render() { RuntimeException exception = null; try { // Invoke prepareForRender only if this is the root component to be rendered MarkupContainer parent = getParent(); if ((parent == null) || (parent.getFlag(FLAG_RENDERING) == false) || isAuto()) { internalPrepareForRender(true); } // Do the render 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 final 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); // Flag: we stated the render process markRendering(true); 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, 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 (Throwable 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 ns = Strings.isEmpty(tag.getNamespace()) ? null : tag.getNamespace() + ':'; response.write("<"); if (ns != null) { response.write(ns); } response.write(tag.getName()); response.write(" id=\""); response.write(getAjaxRegionMarkupId()); response.write("\" style=\"display:none\">"); } /** * 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. */ public 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 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 if (getRenderBodyOnly() == false) { 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 (tag.isOpen()) { if (openTag.isOpen()) { renderClosingComponentTag(markupStream, tag, getRenderBodyOnly()); } else if (getRenderBodyOnly() == false) { if (needToRenderTag(openTag)) { // Close the manually opened tag. And since the user might have changed the // tag name ... getResponse().write(tag.syntheticCloseTagString()); } } } } 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 than 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.html.IHeaderResponse)} *

    * * @param container * The HtmlHeaderContainer */ public void renderHead(final HtmlHeaderContainer container) { if (isVisibleInHierarchy() && isRenderAllowed()) { if (log.isDebugEnabled()) { log.debug("renderHead: " + toString(false)); } IHeaderResponse response = container.getHeaderResponse(); // Allow component to contribute if (response.wasRendered(this) == false) { // Make sure the markup source strategy contributes to the header first // to be backward compatible. WICKET-3761 getMarkupSourcingStrategy().renderHead(this, container); // Then let the component itself to contribute to the header renderHead(this, response); response.markRendered(this); } // Than ask all behaviors for (Behavior behavior : getBehaviors()) { if (isBehaviorAccepted(behavior)) { if (response.wasRendered(behavior) == false) { behavior.renderHead(this, response); response.markRendered(behavior); } } } } } /** * 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) { if (replacement == null) { throw new IllegalArgumentException("Argument [[replacement]] cannot be null."); } 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"); } if (markupId instanceof Integer) { generatedMarkupId = (Integer)markupId; setMetaData(MARKUP_ID_KEY, null); return; } generatedMarkupId = -1; setMetaData(MARKUP_ID_KEY, (String)markupId); setOutputMarkupId(true); } /** * 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 */ public final void setMetaData(final MetaDataKey key, final M object) { MetaDataEntry[] old = getMetaData(); Object metaData = null; MetaDataEntry[] metaDataArray = key.set(getMetaData(), 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); } } /** * 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(); // Detach current model if (prevModel != null) { prevModel.detach(); } IModel wrappedModel = prevModel; if (prevModel instanceof IWrapModel) { wrappedModel = ((IWrapModel)prevModel).getWrappedModel(); } // Change model if (wrappedModel != model) { if (wrappedModel != null) { addStateChange(); } setModelImpl(wrap(model)); } modelChanged(); 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); // WICKET-3413 reset 'inherited model' flag if model changed // and a new one is not IComponentInheritedModel if (getFlag(FLAG_INHERITABLE_MODEL) && !(model instanceof IComponentInheritedModel)) { setFlag(FLAG_INHERITABLE_MODEL, false); } } 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 */ @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 model object on null model of 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(); model.setObject(object); 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 style="display:none;" 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, 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 Page 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(getPage().getClass().getName()) .append(", path = ") .append(getPath()) .append('.') .append(Classes.simpleName(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. IBehaviorListener on * AjaxPagingNavigationBehavior). * * @param behaviour * The behavior that the URL should point to * @param listener * The listener interface that the URL should call * @return The URL * @deprecated use {@link #urlFor(Behavior, RequestListenerInterface, PageParameters)} with * explit parameters */ @Deprecated public final CharSequence urlFor(final Behavior behaviour, final RequestListenerInterface listener) { return urlFor(behaviour, listener, null); } /** * Gets a URL for the listener interface on a behavior (e.g. IBehaviorListener on * AjaxPagingNavigationBehavior). * * @param behaviour * The behavior that the URL should point to * @param listener * The listener interface that the URL should call * @param parameters * The parameters that should be rendered into the urls * @return The URL */ public final CharSequence urlFor(final Behavior behaviour, final RequestListenerInterface listener, final PageParameters parameters) { int id = getBehaviorId(behaviour); Page page = getPage(); PageAndComponentProvider provider = new PageAndComponentProvider(page, this, parameters); IRequestHandler handler; if (page.isPageStateless()) { handler = new BookmarkableListenerInterfaceRequestHandler(provider, listener, id); } else { handler = new ListenerInterfaceRequestHandler(provider, listener, id); } return getRequestCycle().urlFor(handler); } /** * 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 the listener interface (e.g. ILinkListener). * * @see RequestCycle#urlFor(IRequestHandler) * * @param listener * The listener interface that the URL should call * @return The URL * @deprecated use {@link #urlFor(RequestListenerInterface, PageParameters)} with explicit page * parameters */ @Deprecated public final CharSequence urlFor(final RequestListenerInterface listener) { return urlFor(listener, null); } /** * Gets a URL for the listener interface (e.g. ILinkListener). * * @see RequestCycle#urlFor(IRequestHandler) * * @param listener * The listener interface that the URL should call * @param parameters * The parameters that should be rendered into the urls * @return The URL */ public final CharSequence urlFor(final RequestListenerInterface listener, final PageParameters parameters) { Page page = getPage(); PageAndComponentProvider provider = new PageAndComponentProvider(page, this, parameters); IRequestHandler handler; if (page.isPageStateless()) { handler = new BookmarkableListenerInterfaceRequestHandler(provider, listener); } else { handler = new ListenerInterfaceRequestHandler(provider, listener); } 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 container, calling the visitor's * visit method at each one. * * @param * @param c * 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 c, final IVisitor visitor) { // Start here Component current = getParent(); Visit visit = new Visit(); // Walk up containment hierarchy while (current != null) { // Is current an instance of this class? if (c.isInstance(current)) { visitor.component(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 */ public final void warn(final Serializable message) { getSession().getFeedbackMessages().warn(this, message); getSession().dirty(); } /** * {@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 value * 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 value) { if (key != null) { final String tagAttributeValue = tag.getAttributes().getString(key); if (tagAttributeValue == null || !value.equalsIgnoreCase(tagAttributeValue)) { String msg = String.format("Component [%s] (path = [%s]) must be applied to a tag " + "with [%s] attribute matching [%s], not [%s]", getId(), getPath(), key, value, 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 (!component.isAuto() && getFlag(FLAG_RENDERING)) { 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(); } } /** * Prefixes 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, null is returned. * * @return The Page or 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 a * unmodifiable list. Returns an empty list rather than null 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 a * unmodifiable list or null * @param * A class derived from IBehavior */ public List getBehaviors(Class type) { return new Behaviors(this).getBehaviors(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. This gives FormComponent, for example, an opportunity to instantiate a model * on the fly using the containing Form's model. * * @return The model */ protected IModel initModel() { IModel foundModel = null; // Search parents for CompoundPropertyModel for (Component current = getParent(); current != null; current = current.getParent()) { // Get model // Don't call the getModel() that could initialize many inbetween // completely useless models. // IModel model = current.getModel(); 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); } /** * @return Component's markup stream * @deprecated Use findMarkupStream() instead. */ @Deprecated protected MarkupStream locateMarkupStream() { return new MarkupStream(getMarkup()); } /** * Called just after a component is rendered. */ protected void onAfterRender() { setFlag(FLAG_AFTER_RENDERING, false); } /** * Called just before a 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. *

    */ protected void onBeforeRender() { setFlag(FLAG_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()); } if (getApplication().getDebugSettings().isOutputComponentPath()) { String path = getPageRelativePath(); path = path.replace("_", "__"); path = path.replace(":", "_"); tag.put("wicketpath", 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() { setFlag(FLAG_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() { setFlag(FLAG_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()) { 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)) { if (getFlag(FLAG_OUTPUT_MARKUP_ID)) { log.warn(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(), getId(), tag.getName())); } if (getFlag(FLAG_PLACEHOLDER)) { log.warn(String.format( "Placeholder tag set on a component that is usually not rendered into markup. " + "Component id: %s, component tag: %s.", getId(), tag.getName())); } } // 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 void setAuto(final boolean auto) { setFlag(FLAG_AUTO, auto); } /** * 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 void setFlag(final int flag, final boolean set) { if (set) { flags |= flag; } else { flags &= ~flag; } } /** * 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 void setRequestFlag(final short flag, final boolean set) { if (set) { requestFlags |= flag; } else { requestFlags &= ~flag; } } /** * 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 */ 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)); } /** * Checks whether or not this component has a markup id value generated, whether it is automatic * or user defined * * @return true if this component has a markup id value generated */ final boolean hasMarkupIdMetaData() { return getMarkupId() != null; } /** * @param setRenderingFlag * rendering flag */ void internalMarkRendering(boolean setRenderingFlag) { if (setRenderingFlag) { setFlag(FLAG_PREPARED_FOR_RENDER, false); setFlag(FLAG_RENDERING, true); } else { setFlag(FLAG_RENDERING, false); } } /** * @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 getFlag(FLAG_PREPARED_FOR_RENDER); } /** * */ protected void onAfterRenderChildren() { } /** * 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)) { getResponse().write(openTag.syntheticCloseTagString()); } } 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); } } } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT! * * Sets the id of this component. * * @param id * The non-null id of this component */ final void setId(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.id = id; } /** * 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) { setFlag(FLAG_VISIBILITY_ALLOWED, allowed); 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 final 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; } /** * TODO WICKET-NG javadoc * * @deprecated use {@link #canCallListenerInterface(Method)} instead */ @Deprecated public final boolean canCallListenerInterface() { return true; } /** * Checks whether or not a listener method can be invoked on this component. Usually components * deny these invocations if they are either invisible or disabled in hierarchy. Components can * examine which listener interface is being invoked by examining the declaring class of the * passed in {@literal method} parameter. *

    * 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 disable * 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} if the listener method belongs to {@link IRequestListener}. *

    * * @param method * listener method about to be invoked on this component. Could be {@code null} - in this * case it means any method. * * @return {@literal true} iff the listener method can be invoked on this component */ public boolean canCallListenerInterface(Method method) { return isEnabledInHierarchy() && isVisibleInHierarchy(); } /** * CAUTION: this method is not meant to be overridden like it was in wicket 1.4 when * implementing {@link IHeaderContributor}. overload * {@link Component#renderHead(org.apache.wicket.markup.html.IHeaderResponse)} instead to * contribute to the response header. * * @param component * @param response */ public final void renderHead(Component component, IHeaderResponse response) { if (component != this) { throw new IllegalStateException( "This method is only meant to be invoked on the component where the parameter component==this"); } renderHead(response); } /** * Render to the web response whatever the component wants to contribute to the head section. * * @param response * Response object */ public void renderHead(IHeaderResponse response) { // noop } /** {@inheritDoc} */ public void onEvent(IEvent event) { } /** {@inheritDoc} */ public final void send(IEventSink sink, Broadcast type, T payload) { 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) { Behaviors helper = new Behaviors(this); for (Behavior behavior : behaviors) { helper.remove(behavior); } return this; } /** {@inheritDoc} */ public final Behavior getBehaviorById(int id) { return new Behaviors(this).getBehaviorById(id); } /** {@inheritDoc} */ public final int getBehaviorId(Behavior behavior) { return new Behaviors(this).getBehaviorId(behavior); } /** * Adds a behavior modifier to the component. * *

    * Note: this method is override to enable users to do things like discussed in this * thread. *

    * * @param behaviors * The behavior modifier(s) to be added * @return this (to allow method call chaining) */ public Component add(final Behavior... behaviors) { new Behaviors(this).add(behaviors); return this; } /** * Gets the currently coupled {@link Behavior}s as a unmodifiable list. Returns an empty list * rather than null if there are no behaviors coupled to this component. * * @return The currently coupled behaviors as a unmodifiable list */ public final List getBehaviors() { return getBehaviors(Behavior.class); } }