001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.behavior;
018
019import org.apache.wicket.Application;
020import org.apache.wicket.Component;
021import org.apache.wicket.IComponentAwareEventSink;
022import org.apache.wicket.IRequestListener;
023import org.apache.wicket.event.IEvent;
024import org.apache.wicket.markup.ComponentTag;
025import org.apache.wicket.markup.head.IHeaderResponse;
026import org.apache.wicket.markup.html.IComponentAwareHeaderContributor;
027import org.apache.wicket.markup.parser.XmlTag.TagType;
028import org.apache.wicket.util.io.IClusterable;
029import org.apache.wicket.util.lang.Args;
030import org.danekja.java.util.function.serializable.SerializableBiConsumer;
031import org.danekja.java.util.function.serializable.SerializableFunction;
032
033/**
034 * Behaviors are kind of plug-ins for Components. They allow functionality to be added to a
035 * component and get essential events forwarded by the component. They can be bound to a concrete
036 * component (using the bind method which is called when the behavior is attached), but they don't
037 * need to. They can modify the components markup by changing the rendered ComponentTag. Behaviors
038 * can have their own models as well, and they are notified when these are to be detached by the
039 * component.
040 * <p>
041 * You also cannot modify a components model with a behavior.
042 * </p>
043 * 
044 * @see IRequestListener
045 * @see org.apache.wicket.markup.html.IHeaderContributor
046 * @see org.apache.wicket.behavior.AbstractAjaxBehavior
047 * @see org.apache.wicket.AttributeModifier
048 * 
049 * @author Ralf Ebert
050 * @author Eelco Hillenius
051 * @author Igor Vaynberg (ivaynberg)
052 */
053public abstract class Behavior
054        implements
055                IClusterable,
056                IComponentAwareEventSink,
057                IComponentAwareHeaderContributor
058{
059        private static final long serialVersionUID = 1L;
060
061        /**
062         * Constructor
063         */
064        public Behavior()
065        {
066                if (Application.exists())
067                {
068                        Application.get().getBehaviorInstantiationListeners().onInstantiation(this);
069                }
070        }
071
072        /**
073         * Called when a component is about to render.
074         * 
075         * @param component
076         *            the component that has this behavior coupled
077         */
078        public void beforeRender(Component component)
079        {
080        }
081
082        /**
083         * Called when a component that has this behavior coupled was rendered.
084         * 
085         * @param component
086         *            the component that has this behavior coupled
087         */
088        public void afterRender(Component component)
089        {
090        }
091
092        /**
093         * Bind this handler to the given component. This method is called by the host component
094         * immediately after this behavior is added to it. This method is useful if you need to do
095         * initialization based on the component it is attached and you can't wait to do it at render
096         * time. Keep in mind that if you decide to keep a reference to the host component, it is not
097         * thread safe anymore, and should thus only be used in situations where you do not reuse the
098         * behavior for multiple components.
099         * 
100         * @param component
101         *            the component to bind to
102         */
103        public void bind(Component component)
104        {
105        }
106
107        /**
108         * Notifies the behavior it is removed from the specified component
109         * 
110         * @param component
111         *            the component this behavior is unbound from
112         */
113        public void unbind(Component component)
114        {
115        }
116
117        /**
118         * Allows the behavior to detach any state it has attached during request processing.
119         * 
120         * @param component
121         *            the component that initiates the detachment of this behavior
122         */
123        public void detach(Component component)
124        {
125        }
126
127        /**
128         * In case an unexpected exception happened anywhere between
129         * {@linkplain #onComponentTag(org.apache.wicket.Component, org.apache.wicket.markup.ComponentTag)} and
130         * {@linkplain #afterRender(org.apache.wicket.Component)},
131         * onException() will be called for any behavior. Typically, if you clean up resources in
132         * {@link #afterRender(Component)}, you should do the same in the implementation of this method.
133         * 
134         * @param component
135         *            the component that has a reference to this behavior and during which processing
136         *            the exception occurred
137         * @param exception
138         *            the unexpected exception
139         */
140        public void onException(Component component, RuntimeException exception)
141        {
142        }
143
144        /**
145         * This method returns false if the behavior generates a callback url (for example ajax
146         * behaviors)
147         * 
148         * @param component
149         *            the component that has this behavior coupled.
150         * 
151         * @return boolean true or false.
152         */
153        public boolean getStatelessHint(Component component)
154        {
155                if (this instanceof IRequestListener)
156                {
157                        // this behavior implements a callback interface, so it cannot be stateless
158                        return false;
159                }
160                return true;
161        }
162
163        /**
164         * Called when a components is rendering and wants to render this behavior. If false is returned
165         * this behavior will be ignored.
166         * 
167         * @param component
168         *            the component that has this behavior coupled
169         * 
170         * @return true if this behavior must be executed/rendered
171         */
172        public boolean isEnabled(Component component)
173        {
174                return true;
175        }
176
177        /**
178         * Called any time a component that has this behavior registered is rendering the component tag.
179         * 
180         * @param component
181         *            the component that renders this tag currently
182         * @param tag
183         *            the tag that is rendered
184         */
185        public void onComponentTag(Component component, ComponentTag tag)
186        {
187        }
188
189        /**
190         * Specifies whether or not this behavior is temporary. Temporary behaviors are removed at the
191         * end of request and never reattached. Such behaviors are useful for modifying component
192         * rendering only when it renders next. Usecases include javascript effects, initial clientside
193         * dom setup, etc.
194         * 
195         * @param component
196         * 
197         * @return true if this behavior is temporary
198         */
199        public boolean isTemporary(Component component)
200        {
201                return false;
202        }
203
204        /**
205         * Checks whether or not an {@link IRequestListener} can be invoked on this behavior. For further
206         * information please read the javadoc on {@link Component#canCallListener()},
207         * this method has the same semantics.
208         * 
209         * WARNING: Read the javadoc of {@link Component#canCallListener()} for important
210         * security-related information.
211         * 
212         * @param component
213         *            component this behavior is attached to
214         * @return {@literal true} iff the listener method can be invoked
215         */
216        public boolean canCallListener(Component component)
217        {
218                return isEnabled(component) && component.canCallListener();
219        }
220
221
222        /**
223         * Render to the web response whatever the component wants to contribute to the head section.
224         * 
225         * @param component
226         * 
227         * @param response
228         *            Response object
229         */
230        @Override
231        public void renderHead(Component component, IHeaderResponse response)
232        {
233        }
234
235        /**
236         * Called immediately after the onConfigure method in a component. Since this is before the
237         * rendering cycle has begun, the behavior can modify the configuration of the component (i.e.
238         * setVisible(false))
239         * 
240         * @param component
241         *            the component being configured
242         */
243        public void onConfigure(Component component)
244        {
245        }
246
247        /**
248         * Called to notify the behavior about any events sent to the component
249         * 
250         * @see org.apache.wicket.IComponentAwareEventSink#onEvent(org.apache.wicket.Component,
251         *      org.apache.wicket.event.IEvent)
252         */
253        @Override
254        public void onEvent(Component component, IEvent<?> event)
255        {
256        }
257
258        /**
259         * Called to notify that the component is being removed from its parent
260         * @param component
261         *      the removed component
262         */
263        public void onRemove(Component component)
264        {
265        }
266
267        /**
268         * Creates a {@link Behavior} that uses the given {@code SerializableConsumer consumer} to do
269         * something with the component's tag.
270         *
271         * <p>
272         * Usage:<br/>
273         * <code>component.add(onTag(tag -&gt; tag.put(key, value)));</code>
274         * </p>
275         *
276         * @param onTagConsumer
277         *            the {@code SerializableConsumer} that accepts the {@link ComponentTag}
278         * @return The created behavior
279         */
280        public static Behavior onTag(SerializableBiConsumer<Component, ComponentTag> onTagConsumer)
281        {
282                Args.notNull(onTagConsumer, "onTagConsumer");
283
284                return new Behavior()
285                {
286                        @Override
287                        public void onComponentTag(Component component, ComponentTag tag)
288                        {
289                                onTagConsumer.accept(component, tag);
290                        }
291                };
292        }
293
294        /**
295         * Creates a {@link Behavior} that uses the given {@code SerializableFunction function} to do
296         * something with a component's attribute.
297         *
298         * <p>
299         * Usage:<br/>
300         * <code>component.add(onAttribute("class",
301         *              currentValue -&gt; condition(currentValue) ? "positive" : "negative"));</code>
302         * </p>
303         *
304         * @param name
305         *            the name of the attribute to manipulate
306         * @param onAttribute
307         *            the {@code SerializableFunction} that accepts the old value of the attribute and
308         *            returns a new value
309         * @return The created behavior
310         */
311        public static Behavior onAttribute(String name,
312                SerializableFunction<String, CharSequence> onAttribute)
313        {
314                Args.notEmpty(name, "name");
315                Args.notNull(onAttribute, "onAttribute");
316
317                return new Behavior()
318                {
319                        private static final long serialVersionUID = 1L;
320
321                        @Override
322                        public void onComponentTag(Component component, ComponentTag tag)
323                        {
324                                if (tag.getType() != TagType.CLOSE)
325                                {
326                                        String oldValue = tag.getAttribute(name);
327                                        tag.put(name, onAttribute.apply(oldValue));
328                                }
329                        }
330                };
331        }
332
333}