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.Application;
022import org.apache.wicket.AttributeModifier;
023import org.apache.wicket.Component;
024import org.apache.wicket.request.IRequestCycle;
025import org.apache.wicket.request.IRequestHandler;
026import org.apache.wicket.request.cycle.RequestCycle;
027import org.apache.wicket.request.http.WebResponse;
028import org.apache.wicket.util.lang.Args;
029
030
031/**
032 * This behavior builds on top of {@link AbstractAutoCompleteBehavior} by introducing the concept of
033 * a {@link IAutoCompleteRenderer} to make response writing easier.
034 * 
035 * @param <T>
036 * 
037 * @see IAutoCompleteRenderer
038 * 
039 * @since 1.2
040 * 
041 * @author Igor Vaynberg (ivaynberg)
042 * @author Janne Hietam&auml;ki (jannehietamaki)
043 */
044public abstract class AutoCompleteBehavior<T> extends AbstractAutoCompleteBehavior
045{
046        private static final long serialVersionUID = 1L;
047
048        private final IAutoCompleteRenderer<T> renderer;
049
050        /**
051         * Constructor
052         * 
053         * @param renderer
054         *            renderer that will be used to generate output
055         */
056        public AutoCompleteBehavior(final IAutoCompleteRenderer<T> renderer)
057        {
058                this(renderer, false);
059        }
060
061        /**
062         * Constructor
063         * 
064         * @param renderer
065         *            renderer that will be used to generate output
066         * @param preselect
067         *            highlight/preselect the first item in the autocomplete list automatically
068         */
069        public AutoCompleteBehavior(final IAutoCompleteRenderer<T> renderer, final boolean preselect)
070        {
071                this(renderer, new AutoCompleteSettings().setPreselect(preselect));
072        }
073
074        /**
075         * Constructor
076         * 
077         * @param renderer
078         *            renderer that will be used to generate output
079         * @param settings
080         *            settings for the autocomplete list
081         */
082        public AutoCompleteBehavior(final IAutoCompleteRenderer<T> renderer,
083                final AutoCompleteSettings settings)
084        {
085                super(settings);
086
087                this.renderer = Args.notNull(renderer, "renderer");
088        }
089
090        @Override
091        protected void onBind() {
092                super.onBind();
093
094                getComponent().add(new AttributeModifier("aria-autocomplete", "list"));
095        }
096
097        @Override
098        protected final void onRequest(final String val, final RequestCycle requestCycle)
099        {
100                IRequestHandler target = new IRequestHandler()
101                {
102                        @Override
103                        public void respond(final IRequestCycle requestCycle)
104                        {
105                                WebResponse r = (WebResponse)requestCycle.getResponse();
106
107                                // Determine encoding
108                                final String encoding = Application.get()
109                                        .getRequestCycleSettings()
110                                        .getResponseRequestEncoding();
111
112                                r.setContentType("text/xml; charset=" + encoding);
113                                r.disableCaching();
114
115                                Iterator<T> comps = getChoices(val);
116                                int count = 0;
117                                renderer.renderHeader(r);
118                                while (comps.hasNext())
119                                {
120                                        final T comp = comps.next();
121                                        renderer.render(comp, r, val);
122                                        count += 1;
123                                }
124                                renderer.renderFooter(r, count);
125                        }
126                };
127
128                requestCycle.scheduleRequestHandlerAfterCurrent(target);
129        }
130
131        /**
132         * Callback method that should return an iterator over all possible choice objects. These
133         * objects will be passed to the renderer to generate output. Usually it is enough to return an
134         * iterator over strings.
135         * 
136         * @param input
137         *            current input
138         * @return iterator over all possible choice objects
139         */
140        protected abstract Iterator<T> getChoices(String input);
141
142        @Override
143        public void detach(Component component)
144        {
145                renderer.detach();
146        }
147}