/* * 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.behavior; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.IComponentAwareEventSink; import org.apache.wicket.IRequestListener; import org.apache.wicket.event.IEvent; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.html.IComponentAwareHeaderContributor; import org.apache.wicket.markup.parser.XmlTag.TagType; import org.apache.wicket.util.io.IClusterable; import org.apache.wicket.util.lang.Args; import org.danekja.java.util.function.serializable.SerializableBiConsumer; import org.danekja.java.util.function.serializable.SerializableFunction; /** * Behaviors are kind of plug-ins for Components. They allow functionality to be added to a * component and get essential events forwarded by the component. They can be bound to a concrete * component (using the bind method which is called when the behavior is attached), but they don't * need to. They can modify the components markup by changing the rendered ComponentTag. Behaviors * can have their own models as well, and they are notified when these are to be detached by the * component. * <p> * You also cannot modify a components model with a behavior. * </p> * * @see IRequestListener * @see org.apache.wicket.markup.html.IHeaderContributor * @see org.apache.wicket.behavior.AbstractAjaxBehavior * @see org.apache.wicket.AttributeModifier * * @author Ralf Ebert * @author Eelco Hillenius * @author Igor Vaynberg (ivaynberg) */ public abstract class Behavior implements IClusterable, IComponentAwareEventSink, IComponentAwareHeaderContributor { private static final long serialVersionUID = 1L; /** * Constructor */ public Behavior() { if (Application.exists()) { Application.get().getBehaviorInstantiationListeners().onInstantiation(this); } } /** * Called when a component is about to render. * * @param component * the component that has this behavior coupled */ public void beforeRender(Component component) { } /** * Called when a component that has this behavior coupled was rendered. * * @param component * the component that has this behavior coupled */ public void afterRender(Component component) { } /** * Bind this handler to the given component. This method is called by the host component * immediately after this behavior is added to it. This method is useful if you need to do * initialization based on the component it is attached and you can't wait to do it at render * time. Keep in mind that if you decide to keep a reference to the host component, it is not * thread safe anymore, and should thus only be used in situations where you do not reuse the * behavior for multiple components. * * @param component * the component to bind to */ public void bind(Component component) { } /** * Notifies the behavior it is removed from the specified component * * @param component * the component this behavior is unbound from */ public void unbind(Component component) { } /** * Allows the behavior to detach any state it has attached during request processing. * * @param component * the component that initiates the detachment of this behavior */ public void detach(Component component) { } /** * In case an unexpected exception happened anywhere between * {@linkplain #onComponentTag(org.apache.wicket.Component, org.apache.wicket.markup.ComponentTag)} and * {@linkplain #afterRender(org.apache.wicket.Component)}, * onException() will be called for any behavior. Typically, if you clean up resources in * {@link #afterRender(Component)}, you should do the same in the implementation of this method. * * @param component * the component that has a reference to this behavior and during which processing * the exception occurred * @param exception * the unexpected exception */ public void onException(Component component, RuntimeException exception) { } /** * This method returns false if the behavior generates a callback url (for example ajax * behaviors) * * @param component * the component that has this behavior coupled. * * @return boolean true or false. */ public boolean getStatelessHint(Component component) { if (this instanceof IRequestListener) { // this behavior implements a callback interface, so it cannot be stateless return false; } return true; } /** * Called when a components is rendering and wants to render this behavior. If false is returned * this behavior will be ignored. * * @param component * the component that has this behavior coupled * * @return true if this behavior must be executed/rendered */ public boolean isEnabled(Component component) { return true; } /** * Called any time a component that has this behavior registered is rendering the component tag. * * @param component * the component that renders this tag currently * @param tag * the tag that is rendered */ public void onComponentTag(Component component, ComponentTag tag) { } /** * Specifies whether or not this behavior is temporary. Temporary behaviors are removed at the * end of request and never reattached. Such behaviors are useful for modifying component * rendering only when it renders next. Usecases include javascript effects, initial clientside * dom setup, etc. * * @param component * * @return true if this behavior is temporary */ public boolean isTemporary(Component component) { return false; } /** * Checks whether or not an {@link IRequestListener} can be invoked on this behavior. For further * information please read the javadoc on {@link Component#canCallListener()}, * this method has the same semantics. * * WARNING: Read the javadoc of {@link Component#canCallListener()} for important * security-related information. * * @param component * component this behavior is attached to * @return {@literal true} iff the listener method can be invoked */ public boolean canCallListener(Component component) { return isEnabled(component) && component.canCallListener(); } /** * Render to the web response whatever the component wants to contribute to the head section. * * @param component * * @param response * Response object */ @Override public void renderHead(Component component, IHeaderResponse response) { } /** * Called immediately after the onConfigure method in a component. Since this is before the * rendering cycle has begun, the behavior can modify the configuration of the component (i.e. * setVisible(false)) * * @param component * the component being configured */ public void onConfigure(Component component) { } /** * Called to notify the behavior about any events sent to the component * * @see org.apache.wicket.IComponentAwareEventSink#onEvent(org.apache.wicket.Component, * org.apache.wicket.event.IEvent) */ @Override public void onEvent(Component component, IEvent<?> event) { } /** * Called to notify that the component is being removed from its parent * @param component * the removed component */ public void onRemove(Component component) { } /** * Creates a {@link Behavior} that uses the given {@code SerializableConsumer consumer} to do * something with the component's tag. * * <p> * Usage:<br/> * <code>component.add(onTag(tag -> tag.put(key, value)));</code> * </p> * * @param onTagConsumer * the {@code SerializableConsumer} that accepts the {@link ComponentTag} * @return The created behavior */ public static Behavior onTag(SerializableBiConsumer<Component, ComponentTag> onTagConsumer) { Args.notNull(onTagConsumer, "onTagConsumer"); return new Behavior() { @Override public void onComponentTag(Component component, ComponentTag tag) { onTagConsumer.accept(component, tag); } }; } /** * Creates a {@link Behavior} that uses the given {@code SerializableFunction function} to do * something with a component's attribute. * * <p> * Usage:<br/> * <code>component.add(onAttribute("class", * currentValue -> condition(currentValue) ? "positive" : "negative"));</code> * </p> * * @param name * the name of the attribute to manipulate * @param onAttribute * the {@code SerializableFunction} that accepts the old value of the attribute and * returns a new value * @return The created behavior */ public static Behavior onAttribute(String name, SerializableFunction<String, CharSequence> onAttribute) { Args.notEmpty(name, "name"); Args.notNull(onAttribute, "onAttribute"); return new Behavior() { private static final long serialVersionUID = 1L; @Override public void onComponentTag(Component component, ComponentTag tag) { if (tag.getType() != TagType.CLOSE) { String oldValue = tag.getAttribute(name); tag.put(name, onAttribute.apply(oldValue)); } } }; } }