Java tutorial
/* * Copyright (c) 2009 WiQuery team * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.odlabs.wiquery.ui.autocomplete; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.FormComponentPanel; import org.apache.wicket.markup.html.form.HiddenField; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.resource.JavaScriptResourceReference; import org.apache.wicket.util.convert.IConverter; import org.apache.wicket.util.string.Strings; import org.odlabs.wiquery.core.javascript.JsStatement; import org.odlabs.wiquery.core.javascript.JsUtils; import org.odlabs.wiquery.ui.core.JsScopeUiEvent; import java.util.List; /** * $Id: AbstractAutocompleteComponent.java 1143 2011-07-29 11:51:49Z * hielke.hoeve@gmail.com $ * <p> * Base for the autocomplete component * </p> * * @author Julien Roche * @param <T> * The model object type * @since 1.1 */ public abstract class AbstractAutocompleteComponent<T> extends FormComponentPanel<T> { private boolean autoUpdate = false; /** * Inner {@link Autocomplete} * * @author Julien Roche * */ private class InnerAutocomplete<E> extends Autocomplete<E> { // Constants /** Constant of serialization */ private static final long serialVersionUID = -6129719872925080990L; /** * Constructor * * @param id * Wicket identifier * @param model * Model */ public InnerAutocomplete(String id, IModel<E> model) { super(id, model); } @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); response.render( JavaScriptHeaderItem.forReference(WiQueryAutocompleteJavaScriptResourceReference.get())); } @Override protected void onBeforeRender() { onBeforeRenderAutocomplete(this); super.onBeforeRender(); } @Override public Autocomplete<E> setCloseEvent(JsScopeUiEvent close) { throw new WicketRuntimeException("You can't define the close event"); } @Override public Autocomplete<E> setSelectEvent(JsScopeUiEvent select) { throw new WicketRuntimeException("You can't define the select event"); } @Override public Autocomplete<E> setChangeEvent(JsScopeUiEvent select) { throw new WicketRuntimeException("You can't define the change event"); } @Override public Autocomplete<E> setSource(AutocompleteSource source) { throw new WicketRuntimeException("You can't define the source"); } @Override public JsStatement statement() { StringBuilder js = new StringBuilder(); js.append("$.ui.autocomplete.wiquery.changeEvent(event, ui,") .append(JsUtils.quotes(autocompleteHidden.getMarkupId())); if (isAutoUpdate()) { js.append(",'").append(updateAjax.getCallbackUrl()).append('\''); } js.append(");"); super.setChangeEvent(JsScopeUiEvent.quickScope(js.toString())); super.setSelectEvent(JsScopeUiEvent.quickScope(js.append("$(event.target).blur();").toString())); JsStatement jsStatement = super.statement(); return jsStatement; } } // Constants /** Constant of serialization */ private static final long serialVersionUID = -3377109382248062940L; /** Constant of wiQuery Autocomplete resource */ public static final JavaScriptResourceReference WIQUERY_AUTOCOMPLETE_JS = new JavaScriptResourceReference( AutocompleteAjaxComponent.class, "wiquery-autocomplete.js"); // Wicket components private final Autocomplete<String> autocompleteField; private final HiddenField<String> autocompleteHidden; private static final String NOT_ENTERED = "NOT_ENTERED"; /** The choiceRenderer used to generate display/id values for the objects. */ private IChoiceRenderer<? super T> choiceRenderer; private AbstractDefaultAjaxBehavior updateAjax; /** * Constructor * * @param id * Wicket identifier * @param model * Model of the default value */ public AbstractAutocompleteComponent(String id, final IModel<T> model) { super(id, model); setOutputMarkupPlaceholderTag(true); autocompleteHidden = new HiddenField<String>("autocompleteHidden", new Model<String>(NOT_ENTERED) { private static final long serialVersionUID = 1L; @Override public String getObject() { T modelObject = AbstractAutocompleteComponent.this.getModelObject(); if (modelObject != null) { return super.getObject(); } else { return null; } } }); autocompleteHidden.setOutputMarkupId(true); add(autocompleteHidden); autocompleteField = new InnerAutocomplete<String>("autocompleteField", new IModel<String>() { private static final long serialVersionUID = 1L; @Override @SuppressWarnings("unchecked") public String getObject() { T modelObject = AbstractAutocompleteComponent.this.getModelObject(); if (modelObject != null) { T objectValue = (T) choiceRenderer.getDisplayValue(modelObject); Class<T> objectClass = (Class<T>) (objectValue == null ? null : objectValue.getClass()); String displayValue = ""; if (objectClass != null && objectClass != String.class) { final IConverter<T> converter = getConverter(objectClass); displayValue = converter.convertToString(objectValue, getLocale()); } else if (objectValue != null) { displayValue = objectValue.toString(); } return displayValue; } else { return null; } } @Override public void setObject(String object) { } @Override public void detach() { } }); add(autocompleteField); updateAjax = new AbstractDefaultAjaxBehavior() { private static final long serialVersionUID = 1L; @Override protected void respond(AjaxRequestTarget target) { final String hiddenInput = autocompleteHidden.getInput(); final String fieldInput = autocompleteField.getInput(); autocompleteHidden.setConvertedInput(hiddenInput); autocompleteField.setConvertedInput(fieldInput); validate(); if (isValid()) { updateModel(); onUpdate(target); } } }; add(updateAjax); } public AbstractAutocompleteComponent(String id, final IModel<T> model, IChoiceRenderer<? super T> renderer) { this(id, model); this.setChoiceRenderer(renderer); } /** * Called when the value has been updated via ajax * * @param target */ protected void onUpdate(AjaxRequestTarget target) { } @Override public final void convertInput() { String valueId = autocompleteHidden.getConvertedInput(); String input = autocompleteField.getConvertedInput(); final T object = this.getModelObject(); final IChoiceRenderer<? super T> renderer = getChoiceRenderer(); if (NOT_ENTERED.equals(valueId)) valueId = null; if (valueId == null && Strings.isEmpty(input)) { setConvertedInput(null); } else if (valueId == null) { setConvertedInput(getValueOnSearchFail(input)); } else if (object == null || input.compareTo((String) renderer.getDisplayValue(object)) != 0) { final List<? extends T> choices = getChoices(); boolean found = false; for (int index = 0; index < choices.size(); index++) { // Get next choice final T choice = choices.get(index); final String idValue = renderer.getIdValue(choice, index + 1); if (idValue.equals(valueId)) { setConvertedInput(choice); found = true; break; } } if (!found) { // if it is still not entered, then it means this field was not touched // so keep the original value if (valueId.equals(NOT_ENTERED)) { setConvertedInput(getModelObject()); } else { setConvertedInput(getValueOnSearchFail(input)); } } } else { setConvertedInput(object); } } protected abstract List<? extends T> getChoices(); /** * @return the autocomplete field */ public Autocomplete<String> getAutocompleteField() { return autocompleteField; } /** * @return Hidden field storing the identifier of the Wicket model */ public HiddenField<String> getAutocompleteHidden() { return autocompleteHidden; } /** * Method called when the input is not empty and the search failed * * @param input * Current input * @return a new value */ public abstract T getValueOnSearchFail(String input); /** * Create an {@link AutocompleteJson} * * @param id * @param obj * @return a new instance of {@link AutocompleteJson} */ @SuppressWarnings("unchecked") protected AutocompleteJson newAutocompleteJson(int id, T obj) { boolean thisOneSelected = obj.equals(getModelObject()); T objectValue = (T) getChoiceRenderer().getDisplayValue(obj); Class<T> objectClass = (Class<T>) (objectValue == null ? null : objectValue.getClass()); String displayValue = ""; if (objectClass != null && objectClass != String.class) { final IConverter<T> converter = getConverter(objectClass); displayValue = converter.convertToString(objectValue, getLocale()); } else if (objectValue != null) { displayValue = objectValue.toString(); } final String idValue = getChoiceRenderer().getIdValue(obj, id); if (thisOneSelected) { autocompleteHidden.setModelObject(idValue); } return new AutocompleteJson(idValue, displayValue); } /** * Call in the onBeforeRender of the autocomplete behavior * * @param autocomplete */ protected void onBeforeRenderAutocomplete(Autocomplete<?> autocomplete) { } public void setChoiceRenderer(IChoiceRenderer<? super T> choiceRenderer) { this.choiceRenderer = choiceRenderer; } public IChoiceRenderer<? super T> getChoiceRenderer() { if (choiceRenderer == null) { choiceRenderer = new ChoiceRenderer<T>(); } return choiceRenderer; } public void setAutoUpdate(boolean autoUpdate) { this.autoUpdate = autoUpdate; } /** * Should this value get sent to the server when it is selected automatically * * @return */ public boolean isAutoUpdate() { return autoUpdate; } }