diff --git a/pom.xml b/pom.xml index 842363610c..f81167238f 100644 --- a/pom.xml +++ b/pom.xml @@ -1195,7 +1195,7 @@ https://docs.oracle.com/en/java/javase/17/docs/api/ https://nightlies.apache.org/wicket/apidocs/11.x https://docs.spring.io/spring-framework/docs/6.0.x/javadoc-api - https://logback.qos.ch/apidocs + https://javadoc.io/doc/ch.qos.logback/logback-classic/1.5.18 https://www.slf4j.org/apidocs ${javadoc.disabled} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java index 5feac31056..28e1bfb567 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java @@ -157,7 +157,7 @@ public JQueryAjaxBehavior(IJQueryAjaxAware source) * Constructor * * @param source {@link Behavior} to which the event - returned by {@link #newEvent()} - will be broadcasted. - * @param duration {@link Duration}. If different than {@link Duration#NONE}, an {@link ThrottlingSettings} will be added with the specified {@link Duration}. + * @param duration {@link Duration}. If different than {@link Duration#ZERO}, an {@link ThrottlingSettings} will be added with the specified {@link Duration}. */ public JQueryAjaxBehavior(IJQueryAjaxAware source, Duration duration) { diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java index 95d2a728f2..aa58d7a869 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java @@ -313,7 +313,7 @@ public static boolean isEmpty(List list) /** * Converts a {@link StringValue} to a list of {@code String} * - * @param value the {@link StringValue} + * @param values the {@link StringValue} * @return a list of {@code String} */ public static List toStringList(StringValue values) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index 6b66866bae..eb200ac20a 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -16,6 +16,7 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.Collections; import java.util.List; import java.util.Locale; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -23,7 +24,7 @@ import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.IModel; import org.apache.wicket.util.convert.IConverter; -import org.apache.wicket.util.lang.Args; +import org.danekja.java.util.function.serializable.SerializableSupplier; import org.wicketstuff.jquery.core.IJQueryWidget; import org.wicketstuff.jquery.core.JQueryBehavior; import org.wicketstuff.jquery.core.renderer.ITextRenderer; @@ -40,7 +41,7 @@ * @author Sebastien Briquet - sebfz1 * @author reiern70 */ -public abstract class AbstractAutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR +public abstract class AbstractAutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR { private static final long serialVersionUID = 1L; @@ -51,6 +52,7 @@ public abstract class AbstractAutoCompleteTextField extends TextField impl private final ITextRenderer renderer; private final IConverter converter; + private final IElementSelectionStrategy elementSelectionStrategy; private final IJQueryTemplate template; private JQueryAbstractTemplateBehavior templateBehavior = null; @@ -108,6 +110,7 @@ public AbstractAutoCompleteTextField(String id, ITextRenderer rendere this.renderer = renderer; this.template = this.newTemplate(); this.converter = this.newConverter(); + this.elementSelectionStrategy = this.newElementSelectionStrategy(); } /** @@ -145,7 +148,8 @@ public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer model, ITextRenderer getRenderer() return this.renderer; } + /** + * Gets the {@link IElementSelectionStrategy} used to identify and resolve selected choices. + * + * @return the selection strategy, never {@code null} + */ + public IElementSelectionStrategy getElementSelectionStrategy() + { + return this.elementSelectionStrategy; + } + // Events // @Override @@ -292,17 +307,14 @@ protected void onComponentTag(final ComponentTag tag) tag.put("autocomplete", "off"); // disable browser's autocomplete } - @Override - public final void onSelect(AjaxRequestTarget target, int index) - { - if (-1 < index && index < this.choices.getObject().size()) - { - T choice = this.choices.getObject().get(index); - - this.setModelObject(choice); - this.onSelected(target); - } - } + @Override + public void onSelect(AjaxRequestTarget target, T choice,String identifier) { + if (choice != null) { + LOG.error("Cannot select choice with ID: {}", identifier); + } + this.setModelObject(choice); + this.onSelected(target); + } /** * Triggered when the user selects an item from results that matched its input @@ -318,7 +330,13 @@ protected void onSelected(AjaxRequestTarget target) @Override public JQueryBehavior newWidgetBehavior(String selector) { - return new AutoCompleteBehavior(selector, this) { // NOSONAR + return new AutoCompleteBehavior(selector, this, new SerializableSupplier>() { + + @Override + public List get() { + return choices != null ? choices.getObject() : Collections.emptyList(); + } + }) { // NOSONAR private static final long serialVersionUID = 1L; @@ -394,6 +412,16 @@ public String convertToString(T value, Locale locale) }; } + /** + * Gets a new {@link IElementSelectionStrategy}. Index-based selection is used by default. + * + * @return the selection strategy + */ + protected IElementSelectionStrategy newElementSelectionStrategy() + { + return IndexBasedElementSelectionStrategy.get(); + } + /** * Gets a new {@link AutoCompleteChoiceModelBehavior} * @@ -401,11 +429,17 @@ public String convertToString(T value, Locale locale) */ private AutoCompleteChoiceModelBehavior newChoiceModelBehavior() { - return new AutoCompleteChoiceModelBehavior(this.renderer, this.template) { // NOSONAR - + return new AutoCompleteChoiceModelBehavior(this.renderer, this.template) // NOSONAR + { private static final long serialVersionUID = 1L; private static final String TERM = "term"; + @Override + protected IElementSelectionStrategy getElementSelectionStrategy() + { + return AbstractAutoCompleteTextField.this.getElementSelectionStrategy(); + } + @Override public List getChoices() { @@ -415,4 +449,34 @@ public List getChoices() } }; } + + /** + * Gets the {@link AutoCompleteChoiceModelBehavior} that serves the choices to the widget + * + * @return the {@link AutoCompleteChoiceModelBehavior}, or {@code null} if the component has not been initialized yet + */ + public final AutoCompleteChoiceModelBehavior getChoiceModelBehavior() + { + return this.choiceModelBehavior; + } + + /** + * Gets the cached choices of the last query, used to resolve the user selected object + * + * @return the {@link IModel} of choices, or {@code null} if no query has been performed yet + */ + public final IModel> getChoices() + { + return this.choices; + } + + /** + * Gets the {@link JQueryAbstractTemplateBehavior} supplied by {@link #newTemplate()} + * + * @return the {@link JQueryAbstractTemplateBehavior}, or {@code null} if there is no template + */ + public final JQueryAbstractTemplateBehavior getTemplateBehavior() + { + return this.templateBehavior; + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java index d2654d90b3..94570865f6 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java @@ -16,10 +16,14 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.List; + import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.CallbackParameter; +import org.apache.wicket.model.IModel; import org.apache.wicket.util.lang.Args; +import org.danekja.java.util.function.serializable.SerializableSupplier; import org.wicketstuff.jquery.core.JQueryEvent; import org.wicketstuff.jquery.core.Options; import org.wicketstuff.jquery.core.ajax.IJQueryAjaxAware; @@ -32,40 +36,45 @@ * * @author Sebastien Briquet - sebfz1 */ -public abstract class AutoCompleteBehavior extends JQueryUIBehavior implements IJQueryAjaxAware +public abstract class AutoCompleteBehavior extends JQueryUIBehavior implements IJQueryAjaxAware { private static final long serialVersionUID = 1L; public static final String METHOD = "autocomplete"; /** event listener */ - private final IAutoCompleteListener listener; + private final IAutoCompleteListener listener; + + /** the model producing values */ + + private final SerializableSupplier> supplier; private JQueryAjaxBehavior onSelectAjaxBehavior = null; /** * Constructor * - * @param selector the html selector (ie: "#myId") + * @param selector the HTML selector (ie: "#myId") * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, IAutoCompleteListener listener) + public AutoCompleteBehavior(String selector, IAutoCompleteListener listener, SerializableSupplier> supplier) { - this(selector, new Options(), listener); + this(selector, new Options(), listener, supplier); } /** * Constructor * - * @param selector the html selector (ie: "#myId") + * @param selector the HTML selector (ie: "#myId") * @param options the {@link Options} * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener) + public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener, SerializableSupplier> supplier) { super(selector, METHOD, options); this.listener = Args.notNull(listener, "listener"); - } + this.supplier = supplier; + } // Methods // @@ -108,16 +117,16 @@ public void onConfigure(Component component) @Override public void onAjax(AjaxRequestTarget target, JQueryEvent event) { - if (event instanceof SelectEvent) + if (event instanceof SelectEvent selectEvent) { - this.listener.onSelect(target, ((SelectEvent) event).getIndex()); + this.listener.onSelect(target, listener.getElementSelectionStrategy().findChoice(supplier.get(), selectEvent.getIdentifier()), selectEvent.getIdentifier()); } } // Factories // /** - * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'select' event + * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'select' eventta * * @param source the {@link IJQueryAjaxAware} * @return a new {@code OnSelectAjaxBehavior} by default @@ -127,7 +136,7 @@ protected JQueryAjaxBehavior newOnSelectAjaxBehavior(IJQueryAjaxAware source) return new OnSelectAjaxBehavior(source); } - // Ajax classes // + // Ajax classes //, /** * Provides a {@link JQueryAjaxBehavior} that aims to be wired to the 'select' event @@ -163,16 +172,31 @@ protected JQueryEvent newEvent() */ protected static class SelectEvent extends JQueryEvent { - private final int index; + private final String identifier; public SelectEvent() { - this.index = RequestCycleUtils.getQueryParameterValue("index").toInt(0); + this.identifier = RequestCycleUtils.getQueryParameterValue("index").toString(); } - public int getIndex() + /** + * Gets the selected item identifier (JSON {@code id}). For the default index strategy this is the list index. + * + * @return the identifier + */ + public String getIdentifier() { - return this.index; + return this.identifier; } } + + /** + * Gets the {@link JQueryAjaxBehavior} wired to the 'select' event + * + * @return the {@code OnSelectAjaxBehavior}, or {@code null} if the behavior has not been bound yet + */ + public final JQueryAjaxBehavior getOnSelectAjaxBehavior() + { + return this.onSelectAjaxBehavior; + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java index 619e4c3829..3953a6fc69 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java @@ -33,7 +33,7 @@ * @param the model object type * @author Sebastien Briquet - sebfz1 */ -abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior +public abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior { private static final long serialVersionUID = 1L; @@ -47,6 +47,16 @@ public AutoCompleteChoiceModelBehavior(ITextRenderer renderer, IJQuer super(renderer, template); } + /** + * Gets the {@link IElementSelectionStrategy}. Index-based selection is used by default. + * + * @return the selection strategy + */ + protected IElementSelectionStrategy getElementSelectionStrategy() + { + return IndexBasedElementSelectionStrategy.get(); + } + @Override protected String getResponse(IRequestParameters parameters) { @@ -61,7 +71,8 @@ protected String getResponse(IRequestParameters parameters) // ITextRenderer // final JSONObject object = this.renderer.render(choice); - object.put("id", Integer.toString(index)); /* 'id' is a reserved word */ + String identifier = this.getElementSelectionStrategy().getIdentifier(choice, index); + object.put("id", identifier != null ? identifier : Integer.toString(index)); /* 'id' is a reserved word */ object.put("value", this.renderer.getText(choice)); /* 'value' is a reserved word */ // Additional properties (like template properties) // diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java index 4c091fc58f..50cebbd539 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java @@ -16,8 +16,12 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.List; + import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.util.io.IClusterable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Event listener shared by the {@link AutoCompleteTextField} widget and the {@link AutoCompleteBehavior} @@ -25,13 +29,40 @@ * @author Sebastien Briquet - sebfz1 * */ -public interface IAutoCompleteListener extends IClusterable +public interface IAutoCompleteListener extends IClusterable { + Logger LOG = LoggerFactory.getLogger(IAutoCompleteListener.class); + + /** + * Gets the {@link IElementSelectionStrategy} used to identify a selected choice and to resolve it from the cached choice list. + * + * @return the {@link IElementSelectionStrategy} + */ + IElementSelectionStrategy getElementSelectionStrategy(); + /** * Triggered when a selection has been made * * @param target the {@link AjaxRequestTarget} - * @param index the index of the selected item + * @param choice the selected choice, or {@code null} if the identifier could not be resolved + * @param identifier the identifier of the selected item (JSON {@code id}; list index by default, or a business id) + */ + void onSelect(AjaxRequestTarget target, T choice, String identifier); + + /** + * Triggered when a selection has been made, using the identifier posted by the client. + *

+ * The default implementation resolves the choice from {@code choiceList} via + * {@link #getElementSelectionStrategy()} and {@link IElementSelectionStrategy#findChoice(List, String)}, + * then delegates to {@link #onSelect(AjaxRequestTarget, Object, String)}. + * + * @param target the {@link AjaxRequestTarget} + * @param choiceList the cached list of choices matching the last query + * @param identifier the identifier of the selected item (JSON {@code id}; list index by default, or a business id) */ - void onSelect(AjaxRequestTarget target, int index); + default void onSelect(AjaxRequestTarget target, List choiceList, String identifier) + { + T choice = getElementSelectionStrategy().findChoice(choiceList, identifier); + onSelect(target, choice, identifier); + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java new file mode 100644 index 0000000000..7a1426d634 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wicketstuff.jquery.ui.form.autocomplete; + +import java.io.Serializable; +import java.util.List; + +import org.apache.wicket.util.io.IClusterable; + +/** + * Strategy used to identify a selected auto-complete choice and to resolve it from the cached choice list. + *

+ * The identifier is written to the JSON {@code id} field and posted back when the user selects an item. + * {@link IndexBasedElementSelectionStrategy} is the default implementation. + *

+ * Implementations must be {@link Serializable}: the strategy is stored on a Wicket component and is + * serialized with the page/session. + * + * @param the choice type + * @author reiern70 + */ +public interface IElementSelectionStrategy extends IClusterable, Serializable +{ + /** + * Gets the identifier that uniquely represents the given choice in the current result set. + * + * @param choice the choice + * @param index the index of the choice in the current result list + * @return the identifier posted to the client (JSON {@code id}) + */ + String getIdentifier(T choice, int index); + + /** + * Resolves the selected choice from the cached list using the identifier posted by the client. + * + * @param choices the cached list of choices + * @param identifier the identifier posted by the client + * @return the matching choice, or {@code null} if none matches + */ + T findChoice(List choices, String identifier); +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..e585f30f73 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wicketstuff.jquery.ui.form.autocomplete; + +import java.util.List; +import java.util.Objects; + +/** + * {@link IElementSelectionStrategy} that identifies choices by an id value. + * Subclasses implement {@link #getValue(Object, int)} to extract that id. + * + * @param the choice type + * @author reiern70 + */ +public abstract class IdBasedElementSelectionStrategy implements IElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + + /** + * Gets the id value that uniquely represents the given choice. + * + * @param choice the choice + * @param index the index of the choice in the current result list + * @return the id value, or {@code null} if none + */ + public abstract String getValue(T choice, int index); + + @Override + public String getIdentifier(T choice, int index) + { + return this.getValue(choice, index); + } + + @Override + public T findChoice(List choices, String identifier) + { + if (choices == null || identifier == null) + { + return null; + } + + for (int index = 0; index < choices.size(); ++index) + { + T choice = choices.get(index); + + if (Objects.equals(identifier, this.getValue(choice, index))) + { + return choice; + } + } + + return null; + } +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..3d6d055340 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wicketstuff.jquery.ui.form.autocomplete; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link IElementSelectionStrategy} that identifies choices by their index in the current result list. + * This is the default strategy used by {@link AbstractAutoCompleteTextField}. + * + * @param the choice type + * @author reiern70 + */ +public class IndexBasedElementSelectionStrategy implements IElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + private static final Logger logger = LoggerFactory.getLogger(IndexBasedElementSelectionStrategy.class); + + private static final IndexBasedElementSelectionStrategy INSTANCE = new IndexBasedElementSelectionStrategy<>(); + + /** + * Gets a shared instance of this strategy. + * + * @param the choice type + * @return the shared strategy + */ + @SuppressWarnings("unchecked") + public static IndexBasedElementSelectionStrategy get() + { + return (IndexBasedElementSelectionStrategy) INSTANCE; + } + + private Object readResolve() + { + return INSTANCE; + } + + @Override + public String getIdentifier(T choice, int index) + { + return Integer.toString(index); + } + + @Override + public T findChoice(List choices, String identifier) + { + if (choices == null || identifier == null) + { + return null; + } + + try + { + int index = Integer.parseInt(identifier); + + if (-1 < index && index < choices.size()) + { + return choices.get(index); + } + } + catch (NumberFormatException e) + { + logger.warn("Failed to parse identifier '{}' as integer for choices lookup.", identifier, e); + return null; + } + + return null; + } +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..63ee541793 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wicketstuff.jquery.ui.form.autocomplete; + +import org.apache.wicket.core.util.lang.PropertyResolver; +import org.apache.wicket.util.lang.Args; + +/** + * {@link IdBasedElementSelectionStrategy} that resolves the id from a bean property expression. + * + * @param the choice type + * @author reiern70 + */ +public class PropertyBasedElementSelectionStrategy extends IdBasedElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + + private final String idExpression; + + /** + * Constructor + * + * @param idExpression the property expression resolved on each choice (e.g. {@code "id"}) + */ + public PropertyBasedElementSelectionStrategy(String idExpression) + { + this.idExpression = Args.notEmpty(idExpression, "idExpression"); + } + + @Override + public String getValue(T choice, int index) + { + if (choice == null) + { + return null; + } + + Object value = PropertyResolver.getValue(this.idExpression, choice); + + return value != null ? value.toString() : null; + } +}