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.Component;
020import org.apache.wicket.IRequestListener;
021import org.apache.wicket.markup.ComponentTag;
022import org.apache.wicket.request.mapper.parameter.INamedParameters;
023import org.apache.wicket.request.mapper.parameter.PageParameters;
024import org.apache.wicket.util.lang.Args;
025
026import java.util.List;
027
028/**
029 * Abstract class for handling Ajax roundtrips. This class serves as a base for javascript specific
030 * implementations, like ones based on Dojo or Scriptaculous, or Wicket's default.
031 * 
032 * @author Eelco Hillenius
033 * @author Ralf Ebert
034 * @author Igor Vaynberg
035 */
036public abstract class AbstractAjaxBehavior extends Behavior implements IRequestListener
037{
038        private static final long serialVersionUID = 1L;
039
040        /** the component that this handler is bound to. */
041        private Component component;
042
043        /**
044         * Constructor.
045         */
046        public AbstractAjaxBehavior()
047        {
048        }
049
050        /**
051         * Bind this handler to the given component.
052         * 
053         * @param hostComponent
054         *            the component to bind to
055         */
056        @Override
057        public final void bind(final Component hostComponent)
058        {
059                Args.notNull(hostComponent, "hostComponent");
060
061                if (component != null)
062                {
063                        throw new IllegalStateException("this kind of handler cannot be attached to " +
064                                "multiple components; it is already attached to component " + component +
065                                ", but component " + hostComponent + " wants to be attached too");
066                }
067
068                component = hostComponent;
069
070                // call the callback
071                onBind();
072        }
073
074        /**
075         * Gets the url that references this handler.
076         * 
077         * @return the url that references this handler
078         */
079        public CharSequence getCallbackUrl()
080        {
081                Component component = getComponent();
082                if (component == null)
083                {
084                        throw new IllegalArgumentException(
085                                "Behavior must be bound to a component to create the URL");
086                }
087
088                PageParameters parameters = new PageParameters();
089                PageParameters pageParameters = component.getPage().getPageParameters();
090                List<INamedParameters.NamedPair> allNamedInPath = pageParameters.getAllNamedByType(INamedParameters.Type.PATH);
091                for (INamedParameters.NamedPair namedPair : allNamedInPath) {
092                        parameters.add(namedPair.getKey(), namedPair.getValue(), INamedParameters.Type.PATH);
093                }
094                return getComponent().urlForListener(this, parameters);
095        }
096
097        @Override
098        public final void onComponentTag(final Component component, final ComponentTag tag)
099        {
100                onComponentTag(tag);
101        }
102
103        @Override
104        public final void afterRender(final Component hostComponent)
105        {
106                onComponentRendered();
107        }
108
109        /**
110         * Gets the component that this handler is bound to.
111         * 
112         * @return the component that this handler is bound to
113         */
114        protected final Component getComponent()
115        {
116                return component;
117        }
118
119        /**
120         * Called any time a component that has this handler registered is rendering the component tag.
121         * Use this method e.g. to bind to javascript event handlers of the tag
122         * 
123         * @param tag
124         *            the tag that is rendered
125         */
126        protected void onComponentTag(final ComponentTag tag)
127        {
128        }
129
130        /**
131         * Called when the component was bound to it's host component. You can get the bound host
132         * component by calling getComponent.
133         */
134        protected void onBind()
135        {
136        }
137
138        /**
139         * Called to indicate that the component that has this handler registered has been rendered. Use
140         * this method to do any cleaning up of temporary state
141         */
142        protected void onComponentRendered()
143        {
144        }
145
146        @Override
147        public final void unbind(Component component)
148        {
149                onUnbind();
150
151                this.component = null;
152
153                super.unbind(component);
154        }
155
156        /**
157         * Called when the behavior is removed from its component. The bound host component is
158         * still available through {@linkplain #getComponent()}. The relation to it will be removed
159         * right after the finish of the execution of this method.
160         */
161        protected void onUnbind()
162        {
163        }
164}