in core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java [340:571]
private record ParameterModel(
int paramIndex,
@NonNull ObjectActionParameter metaModel,
@NonNull ParameterNegotiationModel negotiationModel,
@NonNull _BindableAbstract<ManagedObject> bindableParamValue,
@NonNull BooleanBindable bindableParamValueDirtyFlag,
@NonNull _BindableAbstract<String> bindableParamSearchArgument,
@NonNull LazyObservable<String> observableParamValidation,
@NonNull LazyObservable<Can<ManagedObject>> observableParamChoices,
@NonNull LazyObservable<Consent> observableVisibilityConsent,
@NonNull LazyObservable<Consent> observableUsabilityConsent,
@NonNull Observable<String> observableParamAsTitle,
@NonNull Observable<String> observableParamAsHtml,
@NonNull _Lazy<Bindable<String>> bindableParamAsParsableTextLazy
) implements ManagedParameter {
private ParameterModel(
final int paramIndex,
final @NonNull ParameterNegotiationModel negotiationModel,
final @NonNull ManagedObject initialValue) {
this(paramIndex, negotiationModel.getHead().getMetaModel().getParameterByIndex(paramIndex), negotiationModel,
// bindableParamValue
_Bindables.forValue(initialValue),
// bindableParamValueDirtyFlag
_Bindables.forBoolean(false),
// bindableParamSearchArgument
_Bindables.forValue(null),
// unused in canonical constructor ..
null, null, null, null, null, null, null);
}
// canonical constructor
ParameterModel(
final int paramIndex,
@NonNull final ObjectActionParameter metaModel,
@NonNull final ParameterNegotiationModel negotiationModel,
@NonNull final _BindableAbstract<ManagedObject> bindableParamValue,
@NonNull final BooleanBindable bindableParamValueDirtyFlag,
@NonNull final _BindableAbstract<String> bindableParamSearchArgument,
// unused ..
final LazyObservable<String> observableParamValidation,
final LazyObservable<Can<ManagedObject>> observableParamChoices,
final LazyObservable<Consent> observableVisibilityConsent,
final LazyObservable<Consent> observableUsabilityConsent,
final Observable<String> observableParamAsTitle,
final Observable<String> observableParamAsHtml,
final _Lazy<Bindable<String>> bindableParamAsParsableTextLazy
) {
this.paramIndex = paramIndex;
this.metaModel = metaModel;
this.negotiationModel = negotiationModel;
this.bindableParamValue = bindableParamValue;
this.bindableParamValueDirtyFlag = bindableParamValueDirtyFlag;
this.bindableParamSearchArgument = bindableParamSearchArgument;
bindableParamValue().setValueGuard(MmAssertionUtils.assertInstanceOf(metaModel().getElementType()));
bindableParamValue().addListener((event, oldValue, newValue)->{
if(newValue==null) {
// lift null to empty ...
bindableParamValue().setValue(metaModel().getEmpty()); // triggers this event again
return;
}
negotiationModel().onNewParamValue();
bindableParamValueDirtyFlag().setValue(true); // set dirty whenever an update event happens
});
// has either autoComplete, choices, or none
this.observableParamChoices = metaModel().hasAutoComplete()
? _Observables.lazy(()->
metaModel().getAutoComplete(
negotiationModel(),
bindableParamSearchArgument().getValue(),
InteractionInitiatedBy.USER))
: metaModel().hasChoices()
? _Observables.lazy(()->
getMetaModel().getChoices(negotiationModel(), InteractionInitiatedBy.USER))
: _Observables.lazy(Can::empty);
// if has autoComplete, then activate the search argument
if(metaModel().hasAutoComplete()) {
this.bindableParamSearchArgument.addListener((e,o,n)->{
observableParamChoices().invalidate();
});
}
// validate this parameter, but only when validationFeedback has been activated
this.observableParamValidation = _Observables.lazy(()->
isValidationFeedbackActive()
? negotiationModel().validateImmediately(paramIndex)
: (String)null);
this.observableVisibilityConsent = _Observables.lazy(()->
metaModel().isVisible(
negotiationModel().getHead(),
negotiationModel().getParamValues(),
InteractionInitiatedBy.USER));
this.observableUsabilityConsent = _Observables.lazy(()->
metaModel().isUsable(
negotiationModel().getHead(),
negotiationModel().getParamValues(),
InteractionInitiatedBy.USER));
// value types should have associated rederers via value semantics
this.observableParamAsTitle = _BindingUtil
.bindAsFormated(TargetFormat.TITLE, metaModel(), bindableParamValue());
this.observableParamAsHtml = _BindingUtil
.bindAsFormated(TargetFormat.HTML, metaModel(), bindableParamValue());
// value types should have associated parsers/formatters via value semantics
// except for composite value types, which might have not
this.bindableParamAsParsableTextLazy = _Lazy.threadSafe(()->(Bindable<String>) _BindingUtil
.bindAsFormated(TargetFormat.PARSABLE_TEXT, metaModel(), bindableParamValue()));
}
public void invalidateChoicesAndValidation() {
observableParamChoices.invalidate();
observableParamValidation.invalidate();
}
public void invalidateVisibilityAndUsability() {
observableVisibilityConsent.invalidate();
observableUsabilityConsent.invalidate();
}
private boolean isValidationFeedbackActive() {
return negotiationModel().getObservableValidationFeedbackActive().getValue();
}
// -- MANAGED PARAMETER
@Override
public Identifier getIdentifier() {
return getMetaModel().getFeatureIdentifier();
}
@Override
public String getFriendlyName() {
ObjectActionParameter objectActionParameter = getMetaModel();
return objectActionParameter.getCanonicalFriendlyName();
}
@Override
public Optional<String> getDescription() {
return getMetaModel().getStaticDescription();
}
@Override
public ObjectSpecification getElementType() {
return getMetaModel().getElementType();
}
@Override
public Bindable<ManagedObject> getValue() {
return bindableParamValue;
}
@Override
public Observable<String> getValueAsTitle() {
return observableParamAsTitle;
}
@Override
public Observable<String> getValueAsHtml() {
return observableParamAsHtml;
}
@Override
public boolean isValueAsParsableTextSupported() {
return _BindingUtil.hasParser(metaModel);
}
@Override
public Bindable<String> getValueAsParsableText() {
return bindableParamAsParsableTextLazy.get();
}
@Override
public Observable<String> getValidationMessage() {
return observableParamValidation;
}
@Override
public Bindable<String> getSearchArgument() {
return bindableParamSearchArgument;
}
@Override
public Observable<Can<ManagedObject>> getChoices() {
return observableParamChoices;
}
@Override
public Optional<InteractionVeto> checkUsability(@NonNull final Can<ManagedObject> params) {
try {
var head = negotiationModel().getHead();
var usabilityConsent =
getMetaModel()
.isUsable(head, params, InteractionInitiatedBy.USER);
return usabilityConsent.isVetoed()
? Optional.of(InteractionVeto.readonly(usabilityConsent))
: Optional.empty();
} catch (final Exception ex) {
log.warn(ex.getLocalizedMessage(), ex);
return Optional.of(InteractionVeto.readonly(new Veto("failure during usability evaluation")));
}
}
// -- OBJECT CONTRACT
@Override
public final boolean equals(final Object obj) {
return obj instanceof ParameterModel other
? Objects.equals(this.getIdentifier(), other.getIdentifier())
: false;
}
@Override
public final int hashCode() {
return Objects.hashCode(getIdentifier());
}
@Override
public final String toString() {
return "ParameterModel[id=%s]".formatted(getIdentifier());
}
}