org.apache.wicket.Component Maven / Gradle / Ivy
Show all versions of org.ops4j.pax.wicket.service Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.wicket.ajax.AjaxRequestTarget;
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.IBehavior;
import org.apache.wicket.behavior.IComponentConfigurationBehavior;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.feedback.IFeedback;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupException;
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.internal.HtmlHeaderContainer;
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.model.LoadableDetachableModel;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.settings.IDebugSettings;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.lang.Objects;
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.version.undo.Change;
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 dot 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(). This path is an absolute path beginning with the id of the Page at the root. Pages
* bear a PageMap/Session-relative identifier as their id, so each absolute path will begin with a
* number, such as "0.a.b.c". 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.
*
*
- onInitialize - The {@link #onInitialize()} method is called when a path from this
* component to its parent has been established, usually after the component has been added to its
* parent.
*
*
- 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 Component#onRender(org.apache.wicket.markup.MarkupStream)}. Once this phase begins, a
* Component becomes immutable. Attempts to alter the Component will result in a
* WicketRuntimeException.
*
*
- onDetach () - The {@link Component#onDetach()} method is called.
*
*
* - 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 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(String)},
* {@link Component#info(String)}, {@link Component#warn(String)},
* {@link Component#error(java.io.Serializable)} and {@link Component#fatal(String)} 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. Any
* feedback message for a given Component can be retrieved with {@link Component#getFeedbackMessage}.
*
*
- 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
{
/**
* Change record of a model.
*/
public class ComponentModelChange extends Change
{
private static final long serialVersionUID = 1L;
/** Former model. */
private final IModel> model;
/**
* Construct.
*
* @param model
*/
public ComponentModelChange(IModel> model)
{
super();
this.model = model;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return getClass().getSimpleName() + "[component: " + getPath() + ",model:" + model +
"]";
}
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
setDefaultModel(model);
}
}
/**
* Generic component visitor interface for component traversals.
*
* @param
* The component
*/
public static interface IVisitor
{
/**
* Value to return to continue a traversal.
*/
public static final Object CONTINUE_TRAVERSAL = null;
/**
* A generic value to return to continue a traversal, but if the component is a container,
* don't visit its children.
*/
public static final Object CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER = new Object();
/**
* A generic value to return to stop a traversal.
*/
public static final Object STOP_TRAVERSAL = new Object();
/**
* Called at each component in a traversal.
*
* @param component
* The component
* @return CONTINUE_TRAVERSAL (null) if the traversal should continue, or a non-null return
* value for the traversal method if it should stop. If no return value is useful,
* the generic non-null value STOP_TRAVERSAL can be used.
*/
public Object component(T component);
}
/**
* Change object for undoing addition of behavior
*
* @author Igor Vaynberg (ivaynberg)
*/
private final class AddedBehaviorChange extends Change
{
private static final long serialVersionUID = 1L;
private final IBehavior behavior;
/**
* Construct.
*
* @param behavior
*/
public AddedBehaviorChange(IBehavior behavior)
{
this.behavior = behavior;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "[" + getClass().getName() + " behavior=" + behavior.toString() + "]";
}
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
removeBehavior(behavior);
}
}
/**
* Undo change for component border property
*
* @author ivaynberg
*/
private class ComponentBorderChange extends Change
{
private static final long serialVersionUID = 1L;
private final IComponentBorder old = getComponentBorder();
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
setComponentBorder(old);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return getClass().getSimpleName() + "[component: " + getPath() + ",border:" + old + "]";
}
}
/**
* Change object for undoing removal of behavior
*
* @author Igor Vaynberg (ivaynberg)
*/
private final class RemovedBehaviorChange extends Change
{
private static final long serialVersionUID = 1L;
private final IBehavior behavior;
/**
* Construct.
*
* @param behavior
*/
public RemovedBehaviorChange(IBehavior behavior)
{
this.behavior = behavior;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "[" + getClass().getName() + " behavior=" + behavior.toString() + "]";
}
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
addBehavior(behavior);
}
}
/**
* A enabled change operation.
*/
protected final static class EnabledChange extends Change
{
private static final long serialVersionUID = 1L;
/** Subject. */
private final Component component;
/** Former value. */
private final boolean enabled;
/**
* Construct.
*
* @param component
*/
EnabledChange(final Component component)
{
this.component = component;
enabled = component.getFlag(FLAG_ENABLED);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return getClass().getSimpleName() + "[component: " + component.getPath() +
",enabled: " + enabled + "]";
}
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
component.setEnabled(enabled);
}
}
/**
* A visibility change operation.
*/
protected final static class VisibilityChange extends Change
{
private static final long serialVersionUID = 1L;
/** Subject. */
private final Component component;
/** Former value. */
private final boolean visible;
/**
* Construct.
*
* @param component
*/
VisibilityChange(final Component component)
{
this.component = component;
visible = component.getFlag(FLAG_VISIBLE);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return getClass().getSimpleName() + "[component: " + component.getPath() +
",visible: " + visible + "]";
}
/**
* @see org.apache.wicket.version.undo.Change#undo()
*/
@Override
public void undo()
{
component.setVisible(visible);
}
}
/** 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 key for missing body tags logging. */
private static final MetaDataKey BORDER_KEY = new MetaDataKey()
{
private static final long serialVersionUID = 1L;
};
/** 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);
}
};
/** 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;
/** Reserved subclass-definable flag bit */
protected static final int FLAG_RESERVED6 = 0x20000;
/** Reserved subclass-definable flag bit */
protected static final int FLAG_RESERVED7 = 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;
/** True when a component has been initialized, had {@link #onInitialize()} called */
protected static final int FLAG_INITIALIZED = 0x400000;
/** True when component has been configured, had {@link #onConfigure()} called */
protected static final int FLAG_CONFIGURED = 0x800000;
private static final int FLAG_INITIALIZE_SUPER_CALL_VERIFIED = 0x10000000;
private static final int FLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED = 0x1000000;
/**
* 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_PREPARED_FOR_RENDER = 0x4000000;
private static final int FLAG_RENDERING = 0x2000000;
private static final int FLAG_AFTER_RENDERING = 0x8000000;
/**
* 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_ATTACHED = 0x20000000;
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;
};
/**
* Keeps metadata about the enabled state of the component
*
* The states are: null - not calculated, true and false
*/
private static final MetaDataKey ENABLED_IN_HIERARCHY_CACHE_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;
/** Component id. */
private String id;
/** Any parent container. */
private MarkupContainer parent;
/**
* I really dislike it, but for now we need it. Reason: due to transparent containers and
* IComponentResolver there is guaranteed 1:1 mapping between component and markup
*/
int markupIndex = -1;
/**
* 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;
/**
* 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 IBehavior}(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;
private final int data_length()
{
if (data == null)
{
return 0;
}
else if (data instanceof Object[] && !(data instanceof MetaDataEntry>[]))
{
return ((Object[])data).length;
}
else
{
return 1;
}
}
private 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;
}
}
private 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;
}
}
private final void data_add(Object object)
{
data_insert(-1, object);
}
private 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 before = position;
int after = currentLength - position;
if (before > 0)
{
System.arraycopy(current, 0, array, 0, before);
}
array[position] = object;
if (after > 0)
{
System.arraycopy(current, position, array, position + 1, after);
}
data = array;
}
}
private 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().notifyComponentInstantiationListeners(this);
final IDebugSettings debugSettings = Application.get().getDebugSettings();
if (debugSettings.isLinePreciseReportingOnNewComponentEnabled())
{
setMetaData(CONSTRUCTED_AT_KEY,
Strings.toString(this, new MarkupException("constructed")));
}
if (model != null)
{
setModelImpl(wrap(model));
}
}
/**
* 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 IBehavior... behaviors)
{
if (behaviors == null)
{
throw new IllegalArgumentException("Argument may not be null");
}
for (IBehavior behavior : behaviors)
{
if (behavior == null)
{
throw new IllegalArgumentException("Argument may not be null");
}
addBehavior(behavior);
if (!behavior.isTemporary())
{
addStateChange(new AddedBehaviorChange(behavior));
}
// Give handler the opportunity to bind this component
behavior.bind(this);
}
return this;
}
/**
*
* @param behavior
*/
private void addBehavior(final IBehavior behavior)
{
data_add(behavior);
}
/**
* FOR INTERNAL USE ONLY
*
* @return unmodified list of behaviors which may contain null entries
*/
public final List getBehaviorsRawList()
{
if (data != null)
{
// if the model is set, we must skip it
final int startIndex = getFlag(FLAG_MODEL_SET) ? 1 : 0;
int length = data_length();
if (length > startIndex)
{
final ArrayList result = new ArrayList();
for (int i = startIndex; i < length; ++i)
{
Object o = data_get(i);
if (o == null || o instanceof IBehavior)
{
result.add((IBehavior)o);
}
}
return result;
}
}
return null;
}
/**
* 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);
onAfterRender();
getApplication().notifyComponentOnAfterRenderListeners(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");
}
// always detach children because components can be attached
// independently of their parents
onAfterRenderChildren();
}
finally
{
// this flag must always be set to false.
setFlag(FLAG_RENDERING, false);
}
}
/**
*
*/
private final void internalBeforeRender()
{
configure();
if ((determineVisibility() || callOnBeforeRenderIfNotVisible()) &&
!getFlag(FLAG_RENDERING) && !getFlag(FLAG_PREPARED_FOR_RENDER))
{
setFlag(FLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, false);
getApplication().notifyPreComponentOnBeforeRenderListeners(this);
// clear the enabled in hierarchy cache as it may change as a result of form processing
// or other logic executed in onbeforerender (WICKET-2063)
clearEnabledInHierarchyCache();
onBeforeRender();
getApplication().notifyPostComponentOnBeforeRenderListeners(this);
if (!getFlag(FLAG_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 Object component(Component component)
{
component.beforeRender();
return IVisitor.CONTINUE_TRAVERSAL;
}
});
}
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 (!getFlag(FLAG_CONFIGURED))
{
onConfigure();
setRenderAllowed();
List behaviors = getBehaviors(IComponentConfigurationBehavior.class);
for (IComponentConfigurationBehavior behavior : behaviors)
{
// Components may reject some behavior components
if (isBehaviorAccepted(behavior))
{
behavior.onConfigure(this);
}
}
setFlag(FLAG_CONFIGURED, true);
}
}
/**
* 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 getPage().getPageMap().continueToOriginalDestination();
}
/**
* Registers a debug feedback message for this component
*
* @param message
* The feedback message
*/
public final void debug(final String message)
{
Session.get().getFeedbackMessages().debug(this, message);
Session.get().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");
}
setFlag(FLAG_ATTACHED, false);
setFlag(FLAG_CONFIGURED, false);
// 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
detachBehaviors();
// 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);
}
// clear out enabled state metadata
clearEnabledInHierarchyCache();
// notify any detach listener
IDetachListener detachListener = getApplication().getFrameworkSettings()
.getDetachListener();
if (detachListener != null)
{
detachListener.onDetach(this);
}
}
/**
* THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
*
* Traverses all behaviors and calls detachModel() on them. This is needed to cleanup behavior
* after render. This method is necessary for {@link AjaxRequestTarget} to be able to cleanup
* component's behaviors after header contribution has been done (which is separated from
* component render).
*/
public final void detachBehaviors()
{
for (IBehavior behavior : getBehaviors())
{
// Always detach models, 'accepted' or not. Otherwise, if they
// are accepted during render, but not here - something can go
// undetached, and calling isEnabled can also lead to nasty side
// effects. See for instance Timo's comment on
// http://issues.apache.org/jira/browse/WICKET-673
behavior.detach(this);
if (behavior.isTemporary())
{
removeBehavior(behavior);
}
}
}
/**
* 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)
{
Session.get().getFeedbackMessages().error(this, message);
Session.get().dirty();
}
/**
* Registers an fatal error feedback message for this component
*
* @param message
* The feedback message
*/
public final void fatal(final String message)
{
Session.get().getFeedbackMessages().fatal(this, message);
Session.get().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.hasAssociatedMarkup())
{
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();
}
/**
* Gets the currently coupled {@link IBehavior}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(IBehavior.class);
}
/**
* @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();
}
/**
* @return component border assigned to this component, or null if none
* @deprecated
*/
@Deprecated
public final IComponentBorder getComponentBorder()
{
return getMetaData(BORDER_KEY);
}
/**
* @return nothing, will always throw an exception. Use {@link #getConverter(Class)} instead.
* @deprecated To be removed. Please use/ override {@link #getConverter(Class)} instead.
*/
@Deprecated
public final IConverter getConverter()
{
throw new UnsupportedOperationException("use #getConverter(Class) instead");
}
/**
* 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
*/
public final FeedbackMessage getFeedbackMessage()
{
return Session.get().getFeedbackMessages().messageForComponent(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();
}
/**
* 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()
{
MarkupStream markupStream = locateMarkupStream();
ValueMap attrs = new ValueMap(markupStream.getTag().getAttributes());
attrs.makeImmutable();
return attrs;
}
/**
* Get the markupId
*
* @return MarkupId
*/
public final Object getMarkupIdImpl()
{
if (generatedMarkupId != -1)
{
return new Integer(generatedMarkupId);
}
return getMetaData(MARKUP_ID_KEY);
}
/**
* 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, - not yet working in 1.4] 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;
}
final int generatedMarkupId = storedMarkupId instanceof Integer
? ((Integer)storedMarkupId).intValue() : Session.get().nextSequenceValue();
if (storedMarkupId == null)
{
setMarkupIdImpl(new Integer(generatedMarkupId));
}
// try to read from markup
// TODO getting the id from markup doesn't work everywhere yet.
// unfortunately, we have to drop this until we have a good solution
// for issue http://issues.apache.org/jira/browse/WICKET-694
// markupId = getMarkupAttributes().getString("id");
String markupIdPrefix = "id";
if (!Application.get().getConfigurationType().equals(Application.DEPLOYMENT))
{
// 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();
markupIdPostfix = RequestContext.get().encodeMarkupId(markupIdPostfix);
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;
}
/**
* Calls {@link #getMarkupId(boolean)} with 'true'.
*
* @see #getMarkupId(boolean)
* @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)
{
// Get model value for this component.
return model.getObject();
}
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(String, boolean, boolean)} on the model string returned.
*
* @see Strings#escapeMarkup(String, 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(String, boolean, boolean)} on the model string returned.
*
* @see Strings#escapeMarkup(String, boolean, boolean)
* @see #getEscapeModelStrings()
*
* @param modelObject
* Model object to convert to string
* @return The string
*/
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 IllegalStateException("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 = Objects.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);
}
/**
* Gets the style of this component (see {@link org.apache.wicket.Session}).
*
* @return The style of this component.
*
* @see org.apache.wicket.Session
* @see org.apache.wicket.Session#getStyle()
*/
public final String getStyle()
{
String variation = getVariation();
String style = getSession().getStyle();
if (variation != null && !"".equals(variation))
{
if (style != null && !"".equals(style))
{
style = variation + "_" + style;
}
else
{
style = variation;
}
}
return style;
}
/**
* 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 Session.get().getFeedbackMessages().hasErrorMessageFor(this);
}
/**
* @return True if this component has some kind of feedback message
*/
public final boolean hasFeedbackMessage()
{
return Session.get().getFeedbackMessages().hasMessageFor(this);
}
/**
* Registers an informational feedback message for this component
*
* @param message
* The feedback message
*/
public final void info(final String message)
{
Session.get().getFeedbackMessages().info(this, message);
Session.get().dirty();
}
/**
* @deprecated
*/
// TODO remove after deprecation release
@Deprecated
public final void internalAttach()
{
throw new UnsupportedOperationException();
}
/**
* @deprecated
*/
// TODO remove after deprecation release
@Deprecated
public final void internalDetach()
{
throw new UnsupportedOperationException();
}
/**
* 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;
}
/**
* Returns true if this component is an ancestor of the given component
*
* @param component
* The component to check
* @return True if the given component has this component as an ancestor
* @deprecated use getParent().contains(component, false)
*/
@Deprecated
public final boolean isAncestorOf(final Component component)
{
return getParent().contains(component, false);
}
/**
* @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;
}
for (IBehavior behavior : getBehaviors())
{
if (!behavior.getStatelessHint(this))
{
return false;
}
}
return true;
}
/**
* @return True if this component is versioned
*/
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 #prepareForRender(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 updated component.
*
*
* TODO this method is not part of public api, so rename to internalPrepareForRender
*
* @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 listner request target to build the component
* hierarchy), in that case setRenderingFlag should be false
*
*
*/
public void prepareForRender(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 (int i = 0; i < feedbacks.size(); i++)
{
feedbacks.get(i).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()
{
prepareForRender(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)
{
getPage().getPageMap().redirectToInterceptPage(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 {@link href
* https://issues.apache.org/jira/browse/WICKET-3229}.
*/
public final void remove()
{
if (parent == null)
{
throw new IllegalStateException("Cannot remove " + this + " from null parent!");
}
parent.remove(this);
}
/**
* Removes behavior from component
*
* @param behavior
* behavior to remove
*
* @return this (to allow method call chaining)
*/
public Component remove(final IBehavior behavior)
{
if (behavior == null)
{
throw new IllegalArgumentException("Argument `behavior` cannot be null");
}
if (removeBehavior(behavior))
{
if (!behavior.isTemporary())
{
addStateChange(new RemovedBehaviorChange(behavior));
}
}
else
{
throw new IllegalStateException(
"Tried to remove a behavior that was not added to the component. Behavior: " +
behavior.toString());
}
return this;
}
/**
*
* @param behavior
* @return true if behavior found and removed
*/
private boolean removeBehavior(final IBehavior behavior)
{
final int start = getFlag(FLAG_MODEL_SET) ? 1 : 0;
final int len = data_length();
for (int i = start; i < len; ++i)
{
Object o = data_get(i);
if (o != null && o.equals(behavior))
{
// behaviors that produce urls depend on their index in the behaviors list,
// therefore we cannot blindly shrink the array by removing this behavior's slot.
// Instead we check if there are any behaviors downstream that will be affected by
// this, and if there are we set this behavior's slot to null instead of removing it
// to preserve indexes of behaviors downstream.
boolean anyListenersAfter = false;
for (int j = i + 1; j < len; j++)
{
if (data_get(j) instanceof IRequestListener)
{
anyListenersAfter = true;
break;
}
}
if (anyListenersAfter)
{
data_set(i, null);
}
else
{
data_remove(i);
if (o instanceof IRequestListener)
{
// this was a listener which mightve caused holes in the array, see if we
// can clean them up. notice: at this point we already know there are no
// listeners that can be affected by index change downstream because this is
// the last one in the array
for (int j = i - 1; j >= start; j--)
{
if (data_get(j) == null)
{
data_remove(j);
}
else
{
break;
}
}
}
}
return true;
}
}
return false;
}
/**
* Performs a render of this component as part of a Page level render process.
*
* For component level re-render (e.g. AJAX) please call {@link #renderComponent()}. Though
* render() does seem to work, it will fail for panel children.
*/
public final void render()
{
// Allow currently invisible components to be re-rendered as well
MarkupStream markupStream = null;
if (getParent() != null)
{
markupStream = findMarkupStream();
}
render(markupStream);
}
/**
* Performs a render of this component as part of a Page level render process.
*
* For component level re-render (e.g. AJAX) please call {@link #renderComponent(MarkupStream)}.
* Though render() does seem to work, it will fail for panel children.
*
* @param markupStream
*/
public final void render(final MarkupStream markupStream)
{
// We need to know the index before we do the visibility check.
// Otherwise we wouldn't know the markup index for invisible components
if (markupStream != null)
{
markupIndex = markupStream.getCurrentIndex();
}
markRendering(true);
setMarkupStream(markupStream);
if (markupStream != null)
{
// Guarantee that the markupStream is set and determineVisibility not yet tested
// See WICKET-2049
markupStream.getTag().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
{
// Call implementation to render component
final IComponentBorder border = getComponentBorder();
if (border != null)
{
border.renderBefore(this);
}
notifyBehaviorsComponentBeforeRender();
onRender(markupStream);
notifyBehaviorsComponentRendered();
if (border != null)
{
border.renderAfter(this);
}
// Component has been rendered
rendered();
}
catch (RuntimeException ex)
{
// Call each behaviors onException() to allow the
// behavior to clean up
for (IBehavior behavior : getBehaviors())
{
if (isBehaviorAccepted(behavior))
{
try
{
behavior.exception(this, ex);
}
catch (Throwable ex2)
{
log.error("Error while cleaning up after exception", ex2);
}
}
}
// Re-throw the exception
throw ex;
}
if (log.isDebugEnabled())
{
log.debug("End render " + this);
}
}
// markupStream is null when rendering a page
else if (markupStream != null)
{
if (getFlag(FLAG_PLACEHOLDER))
{
final ComponentTag tag = markupStream.getTag();
renderPlaceholderTag(tag, getResponse());
}
markupStream.skipComponent();
}
}
/**
* 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\">");
if (ns != null)
{
response.write(ns);
}
response.write(tag.getName());
response.write(">");
}
/**
* 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 (IBehavior behavior : getBehaviors())
{
if (behavior instanceof IAjaxRegionMarkupIdProvider)
{
markupId = ((IAjaxRegionMarkupIdProvider)behavior).getAjaxRegionMarkupId(this);
}
}
if (markupId == null)
{
if (this instanceof IAjaxRegionMarkupIdProvider)
{
markupId = ((IAjaxRegionMarkupIdProvider)this).getAjaxRegionMarkupId(this);
}
}
if (markupId == null)
{
markupId = getMarkupId();
}
return markupId;
}
/**
* Page.renderPage() is used to render a whole page. With AJAX however it must be possible to
* render any one component contained in a page. That is what this method is for.
*
* Note: it is not necessary that the page has previously been rendered. But the component must
* have been added (directly or indirectly) to a container with an associated markup file (Page,
* Panel or Border).
*/
public final void renderComponent()
{
// If this Component is a Page
if (this instanceof Page)
{
// Render as Page, with all the special logic that entails
((Page)this).renderPage();
}
else
{
// Save the parent's markup stream to re-assign it at the end
MarkupContainer parent = getParent();
MarkupStream originalMarkupStream = parent.getMarkupStream();
MarkupStream markupStream = locateMarkupStream();
try
{
// Make sure that while rendering the markup stream is found
parent.setMarkupStream(markupStream);
prepareForRender();
// Render the component and all its children
render(markupStream);
}
finally
{
// Make sure the original markup stream is back in place
parent.setMarkupStream(originalMarkupStream);
afterRender();
}
}
}
/**
* 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.
*
* @param markupStream
* The markup stream
*/
public final void renderComponent(final MarkupStream markupStream)
{
markupIndex = markupStream.getCurrentIndex();
// 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
onComponentTagBody(markupStream, tag);
}
// Render close tag
if (tag.isOpen())
{
if (openTag.isOpen())
{
renderClosingComponentTag(markupStream, tag, getRenderBodyOnly());
}
else
{
// If a open-close tag has been modified to be
// open-body-close than a synthetic close tag must be
// rendered.
if (getRenderBodyOnly() == false)
{
final boolean ajaxRequest = getRequest() instanceof WebRequest &&
((WebRequest)getRequest()).isAjax();
final boolean stripWicketTags = ajaxRequest ||
Application.get().getMarkupSettings().getStripWicketTags();
if (!(openTag instanceof WicketTag) || !stripWicketTags)
{
// Close the manually opened tag. And since the
// user might have changed the tag name ...
getResponse().write(tag.syntheticCloseTagString());
}
}
}
}
}
catch (RuntimeException re)
{
if (re instanceof WicketRuntimeException || re instanceof AbortException)
{
throw re;
}
throw new WicketRuntimeException("Exception in rendering component: " + this, re);
}
}
/**
* Called to indicate that a component has been rendered. This method should only very rarely be
* called at all. One usage is in ImageMap, which renders its link children its own special way
* (without calling render() on them). If ImageMap did not 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()
{
// Tell the page that the component rendered
getPage().componentRendered(this);
}
/**
* 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 IHeaderContributor#renderHead(org.apache.wicket.markup.html.IHeaderResponse)}
*
*
* @param container
* The HtmlHeaderContainer
*/
public void renderHead(final HtmlHeaderContainer container)
{
if (isVisibleInHierarchy() && isRenderAllowed())
{
if (this instanceof IHeaderContributor)
{
((IHeaderContributor)this).renderHead(container.getHeaderResponse());
}
// Ask all behaviors if they have something to contribute to the
// header or body onLoad tag.
for (IBehavior behavior : getBehaviors())
{
if (behavior instanceof IHeaderContributor && isBehaviorAccepted(behavior))
{
((IHeaderContributor)behavior).renderHead(container.getHeaderResponse());
}
}
}
}
/**
* 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.
*
* @since 1.2.1
*
* @param replacement
* component to replace this one
*
* TODO 1.5 - this method should return replacement
to facilitate easy
* chaning such as component=component.replaceWith(newcomponent);
*/
public void 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);
}
/**
* @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();
IModel> thatModel = model;
// If both models are non-null they could be the same
if (thisModel != null && thatModel != null)
{
return getInnermostModel(thisModel) == getInnermostModel(thatModel);
}
return false;
}
/**
* Assigns a component border to this component. If called with null
any previous
* border will be cleared.
*
* @param border
* component border to assign, or null
to clear any previous
* @return component for chaining
* @deprecated
*/
@Deprecated
public final Component setComponentBorder(final IComponentBorder border)
{
if (!Objects.equal(getComponentBorder(), border))
{
addStateChange(new ComponentBorderChange());
}
setMetaData(BORDER_KEY, border);
return this;
}
/**
* 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(new EnabledChange(this));
}
}
// Change visibility
setFlag(FLAG_ENABLED, enabled);
onEnabledStateChanged();
}
return this;
}
void clearEnabledInHierarchyCache()
{
setMetaData(ENABLED_IN_HIERARCHY_CACHE_KEY, null);
}
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).intValue();
setMetaData(MARKUP_ID_KEY, null);
return;
}
generatedMarkupId = -1;
setMetaData(MARKUP_ID_KEY, (String)markupId);
}
/**
* 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)
{
if (markupId != null && Strings.isEmpty(markupId))
{
throw new IllegalArgumentException("Markup id cannot be an empty string");
}
// 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) ? (Object)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(new ComponentModelChange(wrappedModel));
}
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
*/
public final boolean isEnabledInHierarchy()
{
Boolean state = getMetaData(ENABLED_IN_HIERARCHY_CACHE_KEY);
if (state == null)
{
Component parent = getParent();
if (parent != null && !parent.isEnabledInHierarchy())
{
state = false;
}
else
{
state = isEnabled() && isEnableAllowed();
}
setMetaData(ENABLED_IN_HIERARCHY_CACHE_KEY, state);
}
return state;
}
}