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 super T> 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 super T> 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
this(id, model, renderer, null);
}
- /**
+
+ /**
* Constructor
*
* @param id the markup id
@@ -160,6 +164,7 @@ public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer
this.renderer = renderer;
this.template = this.newTemplate();
this.converter = this.newConverter();
+ this.elementSelectionStrategy = this.newElementSelectionStrategy();
}
// Methods //
@@ -243,6 +248,16 @@ public ITextRenderer super T> 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 super T> 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