I've found an example of how to bind Integer to a TextField:
Binder<Person> b = new Binder<>();
b.forField(ageField)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("Must be valid integer !"))
.withValidator(integer -> integer > 0, "Age must be positive")
.bind(p -> p.getAge(), (p, i) -> p.setAge(i));
The problem is - there is no StringToCharacterConverter and if have an error if I bind fields as is. The error is:
Property type 'java.lang.Character' doesn't match the field type 'java.lang.String'. Binding should be configured manually using converter.
You need to implement custom converter, here is very simplified version of what could be StringToCharacterConverter for getting the pattern what the they look like:
public class StringToCharacterConverter implements Converter<String,Character> {
#Override
public Result<Character> convertToModel(String value, ValueContext context) {
if (value == null) {
return Result.ok(null);
}
value = value.trim();
if (value.isEmpty()) {
return Result.ok(null);
} else if (value.length() == 1) {
Character character = value.charAt(0);
return Result.ok(character);
} else {
return Result.error("Error message here");
}
}
#Override
public String convertToPresentation(Character value, ValueContext context) {
String string = value.toString();
return string;
}
}
Related
I am trying to implement the following simple imperative logic:
boolean saveImperative(final List list) {
final String existingList = readByNameImperative(list.getName());
if (Objects.isNull(existingList)) {
templateSaveImperative(existingList);
return true;
} else {
templateSaveImperative(existingList);
return false;
}
}
using Project Reactor in declarative way and this is what I was able to achieve:
#Test
public void testDeclarative() {
final Mono<List> list = createList("foo");
final Boolean result = save(list).block();
System.out.println(result);
}
private Mono<Boolean> save(final Mono<List> listMono) {
final Mono<List> existingListMono = listMono.cache()
.flatMap(list -> readByName(list.getName()));
// if
final Mono<List> savedListMono = existingListMono
.flatMap(existingList -> templateSave(Mono.just(existingList)));
final Mono<Boolean> trueResult = savedListMono.map(x -> true);
// else
return trueResult.switchIfEmpty(templateSave(existingListMono).map(x -> false));
}
private Mono<List> templateSave(final Mono<List> listMono) {
return listMono.map(list -> {
System.out.println("templateSave has been called");
return list;
});
}
private Mono<List> readByName(final String listName) {
if (listName != "list001") {
return Mono.empty();
}
return createList(listName);
}
private Mono<List> createList(final String name) {
final List list = List.builder().name(name).build();
return Mono.just(list);
}
#Value
#Builder
private static class List {
private final String name;
}
If I execute the test with list001, it will print:
templateSave has been called
true
as expected, but if I call it with foo, then I got
null
What I would be missing? I would expect an output like:
templateSave has been called
false
in that case.
final Mono<List> existingListMono = listMono.cache()
.flatMap(list -> readByName(list.getName()));
...in your save method, will take your existing list and flat map it using readByName().
Your readByName() method is the following:
private Mono<List> readByName(final String listName) {
if (listName != "list001") {
return Mono.empty();
}
return createList(listName);
}
(I don't believe it's related to this problem, but don't use == or != for comparing strings.)
Since your listName is foo, not list001, it returns an empty Mono - thus existingListMono becomes an empty Mono, and by implication so do savedListMono and trueResult.
When you call your switchIfEmpty() statement however, you pass in templateSave(existingListMono) - and since existingListMono is an empty Mono as above, the save() method returns an empty Mono.
...and when you block on an empty Mono you'll get null - hence the result.
As such, you may wish to use listMono instead of existingListMono in your return statement on the save() method, which will give you the result you're after:
trueResult.switchIfEmpty(templateSave(listMono).map(x -> false))
I have an Entry which holds the Price and I wanted to format it as currency.
Here is the Entry tag
<Entry x:Name="Price" StyleId="Price"
Text="{Binding Model.Price, Converter={StaticResource CurrencyEntryFormatConverter}, Mode=TwoWay}"
Placeholder="{x:Static resx:Resources.PricePlaceholder}"
Style="{StaticResource DefaultEntry}" Keyboard="Numeric"/>
and here is the property in the Model
public decimal Price
{
get
{
return this.price;
}
set
{
if (this.price== value)
{
return;
}
this.price= value;
this.OnPropertyChanged();
}
}
Finally here is the converter:
public class CurrencyEntryFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return value;
}
string result = string.Format(Resources.CurrencyFormatString, (decimal)value);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return 0;
}
string result = value.ToString().Replace(" ", "").Replace("$", "").Replace(",", "");
return result;
}
}
Question: My problem is that when I run the project and try to enter values in the Price field, the code repeats executing between Convert and ConvertBack functions of the converter and application hangs!
Any advice?
In my case the problem was the property implementation
If we define a property of a class which is implementing INotifyPropertyChanged, in order to update the view when the property value changes, we need to call OnPropertyChanged method in the set block:
public decimal Amount
{
get
{
return this.amount;
}
set
{
this.amount = value;
this.OnPropertyChanged(); // like this line
}
}
But just this code like so makes a loop with your bindings. So we need to be checking if the value is different than the current property's value and then if its new, update it. Look at this code:
public decimal Amount
{
get
{
return this.amount;
}
set
{
if (this.amount == value)
{
return;
}
this.amount = value;
this.OnPropertyChanged();
}
}
That if block helps you to stop looping between get and set.
I hope it helps someone.
How can I input a null value in Specflow through a table?
Let's look at an overly simplistic example:
When a tire is attached to a car
| CarId | TireModel | FabricationDate | Batch |
| 1 | Nokian Hakka R | 2015-09-1 | |
The empty string in the Batch column is interpreted as text by specflow and as such, empty string. Is there a special syntax to mark that column as null?
You can create your own IValueRetriever and replace default one with yours
public class StringValueRetriver : IValueRetriever
{
public bool CanRetrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return propertyType == typeof(string);
}
public object Retrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return string.IsNullOrEmpty(keyValuePair.Value) ? null : keyValuePair.Value;
}
}
Some where in your scenario steps
[BeforeScenario]
public void BeforeScenario()
{
Service.Instance.ValueRetrievers.Unregister<TechTalk.SpecFlow.Assist.ValueRetrievers.StringValueRetriever>();
Service.Instance.ValueRetrievers.Register(new StringValueRetriver());
}
older syntax:
[BeforeScenario]
public void BeforeScenario()
{
var defaultStringValueRetriever = Service.Instance.ValueRetrievers.FirstOrDefault(vr => vr is TechTalk.SpecFlow.Assist.ValueRetrievers.StringValueRetriever);
if (defaultStringValueRetriever != null)
{
Service.Instance.UnregisterValueRetriever(defaultStringValueRetriever);
Service.Instance.RegisterValueRetriever(new StringValueRetriver());
}
}
From SpecFlow 3 on-wards, in your Steps class, you can just put the following code. And in the feature file just put null value like this. Now when you use the CreateSet function then it will be deserialized correctly.
Id | Value
1 | <null>
[Binding]
public static class YourStepClass
{
[BeforeTestRun]
public static void BeforeTestRun()
{
Service.Instance.ValueRetrievers.Register(new NullValueRetriever("<null>"));
}
}
I don't believe there is a special syntax for null and I think you'll have to just handle the conversion yourself. The value retrievers have been revised in the v2 branch and you might be able to handle this by deregistering the standard string value retriever and registering your own implementation which looks for some special syntax and returns null.
In the current 1.9.* version though I think you'll just have to check for empty string and return null yourself.
I've just chosen to do this on a case by case manner using a simple extension method.
In the handler I convert the passed in example value parameter and call NullIfEmpty()
Example usage
AndICheckTheBatchNumber(string batch) {
batch = batch.NullIfEmpty();
//use batch as null how you intended
}
Extension method
using System;
namespace Util.Extensions
{
public static class StringExtensions
{
public static string NullIfEmpty(this string str)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
return str;
}
}
}
Combining answers, I did the following:
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;
using TechTalk.SpecFlow.Assist.ValueRetrievers;
namespace Util.Extensions
{
public class NullValueComparer : IValueComparer
{
private readonly string _nullValue;
public NullValueComparer(string nullValue)
{
_nullValue = nullValue;
}
public bool CanCompare(object actualValue)
{
return actualValue is null || actualValue is string;
}
public bool Compare(string expectedValue, object actualValue)
{
if (_nullValue == expectedValue)
{
return actualValue == null;
}
return expectedValue == (string)actualValue;
}
}
}
And referenced it like this:
[Binding]
public class MyStepDefinitions
{
private MyTestDto _testDto;
private AnotherDtoFromElsewhere _actual;
[BeforeScenario]
public void BeforeTestRun()
{
Service.Instance.ValueRetrievers.Register(new NullValueRetriever("<null>"));
Service.Instance.ValueComparers.Register(new NullValueComparer("<null>"));
}
[When(#"Some test with table:")]
public void WhenTestWithTable(Table table)
{
_testDto = table.CreateInstance<MyTestDto>();
var actual = new AnotherDtoFromElsewhere();
table.CompareToInstance(actual);
}
[Then(#"X should match:")]
public void ThenShouldMatch(Table table)
{
table.CompareToInstance(_actual);
}
}
in my application i have a Custom text box with BasicEditField.FILTER_NUMERIC. When the user enter the value in the field the comma should be added to the Currency format .
EX:1,234,567,8.... like this.
In my code i tried like this.
protected boolean keyUp(int keycode, int time) {
String entireText = getText();
if (!entireText.equals(new String(""))) {
double val = Double.parseDouble(entireText);
String txt = Utile.formatNumber(val, 3, ",");// this will give the //comma separation format
setText(txt);// set the value in the text box
}
return super.keyUp(keycode, time);
}
it will give the correct number format... when i set the value in the text box it will through the IllegalArgumentException. I know BasicEditField.FILTER_NUMERIC will not allow the charector like comma(,)..
How can i achieve this?
I tried this way and it works fine...
public class MyTextfilter extends TextFilter {
private static TextFilter _tf = TextFilter.get(TextFilter.REAL_NUMERIC);
public char convert(char character, int status) {
char c = 0;
c = _tf.convert(character, status);
if (c != 0) {
return c;
}
return 0;
}
public boolean validate(char character) {
if (character == Characters.COMMA) {
return true;
}
boolean b = _tf.validate(character);
if (b) {
return true;
}
return false;
}
}
and call like this
editField.setFilter(new MyTextfilter());
The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html