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.extensions.ajax.markup.html.autocomplete;
018
019import java.util.Iterator;
020
021import org.apache.wicket.ConverterLocator;
022import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
023import org.apache.wicket.behavior.Behavior;
024import org.apache.wicket.markup.ComponentTag;
025import org.apache.wicket.markup.html.form.TextField;
026import org.apache.wicket.model.IModel;
027import org.apache.wicket.util.convert.IConverter;
028
029/**
030 * An implementation of a textfield with the autoassist ajax behavior {@link AutoCompleteBehavior}.
031 * <p>
032 * An {@link IAutoCompleteRenderer} is used for rendering of choices. To convert input back into a
033 * non-String type you will have to provide a custom {@link IConverter}, either by overriding
034 * {@link #getConverter(Class)} or by setting a suitable {@link IConverter} on the application's
035 * {@link ConverterLocator}.
036 * <p>
037 * Note that you must add your own CSS to make the suggestion display properly, see
038 * {@link DefaultCssAutoCompleteTextField} for an example.
039 * 
040 * @see DefaultCssAutoCompleteTextField
041 * @see AutoCompleteBehavior
042 * @see IAutoCompleteRenderer
043 * 
044 * @since 1.2
045 * 
046 * @author Igor Vaynberg (ivaynberg)
047 * 
048 * @param <T>
049 *            The model object type
050 */
051public abstract class AutoCompleteTextField<T> extends TextField<T>
052{
053        private static final long serialVersionUID = 1L;
054
055        /** auto complete behavior attached to this textfield */
056        private AutoCompleteBehavior<T> behavior;
057
058        /** renderer */
059        private final IAutoCompleteRenderer<T> renderer;
060
061        /** settings */
062        private final AutoCompleteSettings settings;
063
064        /**
065         * Constructor for the given type with default settings.
066         * 
067         * @param id
068         *            component id
069         * @param type
070         *            model objec type
071         */
072        public AutoCompleteTextField(final String id, final Class<T> type)
073        {
074                this(id, null, type, new AutoCompleteSettings());
075        }
076
077        /**
078         * Constructor for the given model and type.
079         * 
080         * @param id
081         *            component id
082         * @param model
083         *            model
084         * @param type
085         *            model object type
086         * @param settings
087         *            settings for autocomplete
088         */
089        @SuppressWarnings("unchecked")
090        public AutoCompleteTextField(final String id, final IModel<T> model, final Class<T> type,
091                final AutoCompleteSettings settings)
092        {
093                this(id, model, type, StringAutoCompleteRenderer.INSTANCE, settings);
094        }
095
096        /**
097         * Constructor for given model.
098         * 
099         * @param id
100         *            component id
101         * @param model
102         *            model
103         * @param settings
104         *            settings for autocomplete
105         */
106        public AutoCompleteTextField(final String id, final IModel<T> model,
107                final AutoCompleteSettings settings)
108        {
109                this(id, model, null, settings);
110        }
111
112        /**
113         * Constructor for the given model.
114         * 
115         * @param id
116         *            component id
117         * @param model
118         *            model
119         */
120        public AutoCompleteTextField(final String id, final IModel<T> model)
121        {
122                this(id, model, null, new AutoCompleteSettings());
123        }
124
125        /**
126         * Constructor.
127         * 
128         * @param id
129         *            component id
130         * @param settings
131         *            settings for autocomplete
132         */
133        public AutoCompleteTextField(final String id, final AutoCompleteSettings settings)
134        {
135                this(id, null, settings);
136        }
137
138        /**
139         * Constructor.
140         * 
141         * @param id
142         *            component id
143         */
144        public AutoCompleteTextField(final String id)
145        {
146                this(id, null, new AutoCompleteSettings());
147        }
148
149        /**
150         * Constructor using the given renderer.
151         * 
152         * @param id
153         *            component id
154         * @param renderer
155         *            renderer for autocomplete
156         */
157        public AutoCompleteTextField(final String id, final IAutoCompleteRenderer<T> renderer)
158        {
159                this(id, (IModel<T>)null, renderer);
160        }
161
162        /**
163         * Constructor for the given type using the given renderer
164         * 
165         * @param id
166         *            component id
167         * @param type
168         *            model object type
169         * @param renderer
170         *            renderer for autocomplete
171         */
172        public AutoCompleteTextField(final String id, final Class<T> type,
173                final IAutoCompleteRenderer<T> renderer)
174        {
175                this(id, null, type, renderer, new AutoCompleteSettings());
176        }
177
178        /**
179         * Constructor for the given model using the given renderer.
180         * 
181         * @param id
182         *            component id
183         * @param model
184         *            model
185         * @param renderer
186         *            renderer for autocomplete
187         */
188        public AutoCompleteTextField(final String id, final IModel<T> model,
189                final IAutoCompleteRenderer<T> renderer)
190        {
191                this(id, model, null, renderer, new AutoCompleteSettings());
192        }
193
194        /**
195         * Constructor for the given model using the given renderer.
196         * 
197         * @param id
198         *            component id
199         * @param model
200         *            model
201         * @param type
202         *            model object type
203         * @param renderer
204         *            renderer for autocomplete
205         * @param settings
206         *            settings for autocomplete
207         */
208        public AutoCompleteTextField(final String id, final IModel<T> model, final Class<T> type,
209                final IAutoCompleteRenderer<T> renderer, final AutoCompleteSettings settings)
210        {
211                super(id, model, type);
212                this.renderer = renderer;
213                this.settings = settings;
214        }
215
216        /**
217         * Factory method for autocomplete behavior that will be added to this textfield
218         * 
219         * @param renderer
220         *            auto complete renderer
221         * @param settings
222         *            auto complete settings
223         * @return auto complete behavior
224         */
225        protected AutoCompleteBehavior<T> newAutoCompleteBehavior(
226                final IAutoCompleteRenderer<T> renderer, final AutoCompleteSettings settings)
227        {
228                return new AutoCompleteBehavior<>(renderer, settings)
229                {
230                        private static final long serialVersionUID = 1L;
231
232                        @Override
233                        protected Iterator<T> getChoices(final String input)
234                        {
235                                return AutoCompleteTextField.this.getChoices(input);
236                        }
237                        
238                        @Override
239                        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
240                        {
241                                super.updateAjaxAttributes(attributes);
242                                
243                                AutoCompleteTextField.this.updateAjaxAttributes(attributes);
244                        }
245                };
246        }
247
248        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
249        {
250        }
251
252        /**
253         * Initializes the {@link AutoCompleteBehavior} if it is not already there.
254         */
255        @Override
256        protected void onInitialize()
257        {
258                super.onInitialize();
259
260                initializeAutoCompleteBehavior();
261        }
262
263        /**
264         * The {@link AutoCompleteBehavior} is added lazily instead from the constructor to support an
265         * overridable factory method.
266         * 
267         * @see #onInitialize()
268         * @see #add(Behavior...)
269         * @see #newAutoCompleteBehavior(IAutoCompleteRenderer, AutoCompleteSettings)
270         */
271        private void initializeAutoCompleteBehavior()
272        {
273                // add auto complete behavior to this component if its not already there
274                if (behavior == null)
275                {
276                        super.add(behavior = newAutoCompleteBehavior(renderer, settings));
277                }
278        }
279
280        @Override
281        protected void onComponentTag(final ComponentTag tag)
282        {
283                super.onComponentTag(tag);
284
285                // disable browser's autocomplete
286                tag.put("autocomplete", "off");
287        }
288
289        /**
290         * Callback method that should return an iterator over all possible assist choice objects. These
291         * objects will be passed to the renderer to generate output. Usually it is enough to return an
292         * iterator over strings.
293         * 
294         * @see AutoCompleteBehavior#getChoices(String)
295         * 
296         * @param input
297         *            current input
298         * @return iterator over all possible choice objects
299         */
300        protected abstract Iterator<T> getChoices(String input);
301
302        /**
303         * @return The {@link IAutoCompleteRenderer} used to generate html output for the
304         *         {@link AutoCompleteBehavior}.
305         */
306        public final IAutoCompleteRenderer<T> getChoiceRenderer()
307        {
308                return renderer;
309        }
310        
311        @Override
312        protected void onDetach()
313        {
314                renderer.detach();
315                
316                super.onDetach();
317        }
318}