in viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/select2/ChoiceProvider.java [41:233]
record ChoiceProvider(
UiAttributeWkt attributeModel,
UiAttribute.ChoiceProviderSort choiceProviderSort)
implements HasMetaModelContext, Serializable {
public ChoiceProvider(
final UiAttributeWkt attributeModel) {
this(attributeModel, UiAttribute.ChoiceProviderSort.valueOf(attributeModel));
}
/**
* Get the value for displaying to an end user.
*/
public String getDisplayValue(final ObjectMemento choiceMemento) {
if (choiceMemento == null
|| choiceMemento.isEmpty()) {
return getPlaceholderRenderService().asText(PlaceholderLiteral.NULL_REPRESENTATION);
}
return translate(choiceMemento.title());
}
/**
* This method is called to get the id value of an object (used as the value attribute of a
* choice element) The id can be extracted from the object like a primary key, or if the list is
* stable you could just return a toString of the index.
* <p>
* Note that the given index can be {@code -1} if the object in question is not contained in the
* available choices.
*/
public String getIdValue(final ObjectMemento choiceMemento) {
if (choiceMemento == null) return ObjectMemento.NULL_ID;
return ObjectMemento.enstringToUrlBase64(choiceMemento);
}
/**
* Queries application for choices that match the search {@code term} and adds them to the
* {@code response}
*/
public void query(
final String term,
final int page,
final org.wicketstuff.select2.Response<ObjectMemento> response) {
var mementosFiltered = query(term);
if(isRequired()) {
response.addAll(mementosFiltered.toList());
return;
}
// else, if not mandatory, prepend null
var mementosIncludingNull = mementosFiltered.toArrayList();
mementosIncludingNull.add(0, null);
response.addAll(mementosIncludingNull);
}
/**
* Converts a list of choice ids back into application's choice objects. When the choice
* provider is attached to a single-select component the {@code ids} collection will contain
* exactly one id, and a collection containing exactly one choice should be returned.
*/
public Collection<ObjectMemento> toChoices(final Collection<String> ids) {
return _NullSafe.stream(ids)
.map(this::mementoFromIdWithNullHandling)
.collect(Collectors.toList());
}
// -- UTIL
/** adapter method */
org.wicketstuff.select2.ChoiceProvider<ObjectMemento> toSelect2ChoiceProvider() {
var delegate = this;
return new org.wicketstuff.select2.ChoiceProvider<ObjectMemento>() {
private static final long serialVersionUID = 1L;
@Override public Collection<ObjectMemento> toChoices(final Collection<String> ids) {
return delegate.toChoices(ids);
}
@Override public void query(final String term, final int page, final Response<ObjectMemento> response) {
delegate.query(term, page, response);
}
@Override public String getIdValue(final ObjectMemento object) {
return delegate.getIdValue(object);
}
@Override public String getDisplayValue(final ObjectMemento object) {
return delegate.getDisplayValue(object);
}
};
}
// -- HELPER
private Can<ObjectMemento> queryAll() {
return attributeModel().getChoices() // must not return detached entities
.map(ManagedObject::getMementoElseFail);
}
private Can<ObjectMemento> queryWithAutoCompleteUsingObjectSpecification(final String term) {
var autoCompleteAdapters = Facets
.autoCompleteExecute(attributeModel().getElementType(), term);
return autoCompleteAdapters
.map(ManagedObject::getMementoElseFail);
}
private Can<ObjectMemento> queryWithAutoComplete(final String term) {
var attributeModel = attributeModel();
var pendingArgs = attributeModel.isParameter()
? ((UiParameter)attributeModel).getParameterNegotiationModel().getParamValues()
: Can.<ManagedObject>empty();
var pendingArgMementos = pendingArgs
.map(ManagedObject::getMementoElseFail);
if(attributeModel.isParameter()) {
// recover any pendingArgs
var paramModel = (UiParameter)attributeModel;
paramModel
.getParameterNegotiationModel()
.setParamValues(
reconstructPendingArgs(paramModel, pendingArgMementos));
}
return attributeModel
.getAutoComplete(term)
.map(ManagedObject::getMementoElseFail);
}
private Can<ManagedObject> reconstructPendingArgs(
final UiParameter parameterModel,
final Can<ObjectMemento> pendingArgMementos) {
var pendingArgsList = _NullSafe.stream(pendingArgMementos)
.map(getObjectManager()::demementify)
.collect(Can.toCan());
return pendingArgsList;
}
private @Nullable ObjectMemento mementoFromIdWithNullHandling(final String id) {
if(ObjectMemento.NULL_ID.equals(id)) return null;
return mementoFromId(id);
}
/**
* Whether to not prepend <code>null</code> as choice candidate.
*/
private boolean isRequired() {
return attributeModel().isRequired();
}
/**
* Get choice candidates with filtering (don't include <code>null</code>).
*/
private Can<ObjectMemento> query(final String term) {
return switch(choiceProviderSort) {
case CHOICES->filter(term, queryAll());
case AUTO_COMPLETE->queryWithAutoComplete(term);
case OBJECT_AUTO_COMPLETE->queryWithAutoCompleteUsingObjectSpecification(term);
case NO_CHOICES->Can.empty();
};
}
/**
* Filters all choices against a term by using their
* {@link ManagedObject#getTitle() title string}
*
* @param term The term entered by the user
* @param choiceMementos The collections of choices to filter
* @return A list of all matching choices
*/
private Can<ObjectMemento> filter(
final String term,
final Can<ObjectMemento> choiceMementos) {
if (Strings.isEmpty(term)) return choiceMementos;
var translationContext = TranslationContext.empty();
var translator = getTranslationService();
var termLower = term.toLowerCase();
return choiceMementos.filter((final ObjectMemento candidateMemento)->{
var title = translator.translate(translationContext, candidateMemento.title());
return title.toLowerCase().contains(termLower);
});
}
private @Nullable ObjectMemento mementoFromId(final @Nullable String id) {
return ObjectMemento.destringFromUrlBase64(id);
}
}