How to format a string in a TextField without changing it's value with JavaFX - javafx-8

I am trying to change the value of a TextField for display only. Ie - when users attempt to enter phone number, they only enter the digits and when they leave the field, it displays formatted without changing the data in the field.
Let's say I have a TextField for a phone number and it should allow digits only, maximum of 10 characters:
2085551212
I can handle that with a TextFormatter using a UnaryOperator.
UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change change) {
int maxlength = 14;
if(change.getControlText().indexOf('(') == -1) {
maxlength = 10;
}
System.out.println(change);
if (change.getControlText().length() + change.getText().length() >= maxlength) {
int maxPos = maxlength - change.getControlText().length();
change.setText(change.getText().substring(0, maxPos));
}
String text = change.getText();
for (int i = 0; i < text.length(); i++)
if (!Character.isDigit(text.charAt(i)))
return null;
return change;
}
};
However I would like to format the value to be when it's 10 characters long (likely unformatted when != 10):
(208) 555-1212
When I use a TextFormatter to format it, it changes the value of the string to (208) 555-1212. We store only the digits in the database 2085551212.
I attempted this with a StringConverter. But I couldn't make it work. In the toString() method I strip out the formatting, however when I do that my TextField doesn't display.
StringConverter<String> formatter = new StringConverter<String>() {
#Override
public String fromString(String string) {
System.out.println("fromString(): before = " + string);
if (string.length() == 14) {
System.out.println("fromString(): after = " + string);
return string;
} else if (string.length() == 10 && string.indexOf('-') == -1) {
String result = String.format("(%s) %s-%s", string.substring(0, 3), string.substring(3, 6),
string.substring(6, 10));
System.out.println("fromString(): after = " + result);
return result;
} else {
return null;
}
}
#Override
public String toString(String object) {
System.out.println("toString(): before = " + object);
if(object == null) {
return "";
}
Pattern p = Pattern.compile("[\\p{Punct}\\p{Blank}]", Pattern.UNICODE_CHARACTER_CLASS);
Matcher m = p.matcher(object);
object = m.replaceAll("");
System.out.println("toString(): after = " + object);
return object;
}
};
I bound to a TextField like this which I assumed would work:
txtPhone.setTextFormatter(new TextFormatter<String>(formatter, null, filter));
t2.textProperty().bindBidirectional(foo.fooPropertyProperty(), formatter); //I was just testing to see the results in another textfield to see if it would work.
So I am at a loss. I essentially want to allow only digits and then when the user leaves the field present the value in a formatted way - without actually changing the string value that goes to the database.

You are confusing the purpose of toString() and fromString() methods with each other in your converter. toString() converts the value property of your text editor to the displayed text, not the other way around. Try switching the code in these methods and it should work.
The reason why your text field does not display anything after loosing focus is because fromString() method is called and returns null (from the else clause). This commits null to the value property of your editor. The change in value property updates the displayed text (textProperty) by calling toString(null) which changes the text property of your editor to an empty string.
EDIT
Below is my test code that is a follow-up to the discussion in the comments. I reused a large amount of your original code. I created an FXML JavaFX project and defined TextField and Label in FXML file. The TextField accepts user's input and formats it. The Label displays value of the text formatter (only digits) that should go to the database. The value is accessible by calling formatter.valueProperty().get(). I hope it helps.
import java.net.URL;
import java.util.ResourceBundle;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.util.StringConverter;
public class FXMLDocumentController implements Initializable {
// label displays phone number containing only digits (for database)
#FXML private Label label;
/* field displays formatted text (XXX)-XXX-XXXX after user types
10 digits and presses Enter or if the field looses focus */
#FXML private TextField field;
#Override
public void initialize(URL url, ResourceBundle rb) {
UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change change) {
if (!change.isContentChange()) {
/* nothing is added or deleted but change must be returned
* as it contains selection info and caret position
*/
return change;
}
int maxlength = 14;
if (change.getControlText().indexOf('(') == -1) {
maxlength = 10;
}
if (change.getControlNewText().length() > maxlength
|| change.getText().matches("\\D+")) {
// invalid input. Cancel the change
return null;
}
return change;
}
};
StringConverter<String> converter = new StringConverter<String>() {
// updates displayed text from commited value
#Override
public String toString(String commitedText) {
if (commitedText == null) {
// don't change displayed text
return field.getText();
}
if (commitedText.length() == 10 && !commitedText.matches("\\D+")) {
return String.format("(%s) %s-%s", commitedText.substring(0, 3), commitedText.substring(3, 6),
commitedText.substring(6, 10));
} else {
/* Commited text can be either null or 10 digits.
* Nothing else is allowed by fromString() method unless changed directly
*/
throw new IllegalStateException(
"Unexpected or incomplete phone number value: " + commitedText);
}
}
// commits displayed text to value
#Override
public String fromString(String displayedText) {
// remove formatting characters
Pattern p = Pattern.compile("[\\p{Punct}\\p{Blank}]", Pattern.UNICODE_CHARACTER_CLASS);
Matcher m = p.matcher(displayedText);
displayedText = m.replaceAll("");
if (displayedText.length() != 10) {
// user is not done typing the number. Don't commit
return null;
}
return displayedText;
}
};
TextFormatter<String> formatter = new TextFormatter<String>(converter, "1234567890", filter);
field.setTextFormatter(formatter);
label.textProperty().bind(formatter.valueProperty());
}
}

Related

Is there a way to show constraint message

I have class CategoryDTO and i want to show the message "Description can't be null" in rest docs instead of "Must not be null".Though i know i can change this message my creating a constraint properties file and adding below line to it
javax.validation.constraints.NotNull.description=Must not be blank or null
But i want to show the message in NotNull annotation
public class CategoryDTO
{
private String id;
#NotNull(message = "Description can't be null")
#Size(min = 2 , max=30 , message = "Size must be greater than 2 and less than 30")
private String description;
}
Edit:
#Test
void testFindAll()
{
CategoryDTO fruits = new CategoryDTO();
fruits.setDescription("Fruits");
fruits.setId(UUID.randomUUID().toString());
CategoryDTO Nuts = new CategoryDTO();
Nuts.setDescription("Nuts");
Nuts.setId(UUID.randomUUID().toString());
ConstrainedFields fields = new ConstrainedFields(CategoryDTO.class);
BDDMockito.when(categoryService.findAll()).thenReturn(Flux.just(fruits,Nuts));
webTestClient.get().uri(CategoryController.rootURL + "/categories")
.exchange().expectBodyList(CategoryDTO.class).
hasSize(2).consumeWith(WebTestClientRestDocumentationWrapper.document("v1/get-all-categories",
responseFields(
fields.withPath("[]").description("An array of categories"),
fields.withPath("[].id").description("Id of category"),
fields.withPath("[].description").description("Description of category")
)
));
}
By default, REST Docs' ConstraintDescriptions uses a ResourceBundleConstraintDescriptionResolver to obtain a description for each constraint. As its name suggests, it uses a ResourceBundle to provide the descriptions. You can provide your own implementation of ConstraintDescriptionResolver to use a different mechanism. In your case, you want to use the message from the constraint annotation as shown in the following example:
ConstraintDescriptions descriptions = new ConstraintDescriptions(CategoryDTO.class, (constraint) -> {
return (String) constraint.getConfiguration().get("message");
});
List<String> descriptionProperty = descriptions.descriptionsForProperty("description");
System.out.println(descriptionProperty);
When executed, the above will output the following:
[Description can't be null, Size must be greater than 2 and less than 30]
If you don't always configure the message attribute, you may want to fall back to the resource bundle resolver, as shown in the following example:
ResourceBundleConstraintDescriptionResolver fallback = new ResourceBundleConstraintDescriptionResolver();
ConstraintDescriptions descriptions = new ConstraintDescriptions(CategoryDTO.class, (constraint) -> {
String message = (String) constraint.getConfiguration().get("message");
if (message != null) {
return message;
}
return fallback.resolveDescription(constraint);
});
With the help of Andy's answer here is the final outcome
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.Attributes.key;
import java.util.regex.Pattern;
import org.springframework.restdocs.constraints.ConstraintDescriptions;
import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.util.StringUtils;
public class ConstrainedFields
{
private final ConstraintDescriptions constraintDescriptions;
public ConstrainedFields(Class<?> input) {
ResourceBundleConstraintDescriptionResolver fallback = new ResourceBundleConstraintDescriptionResolver();
this.constraintDescriptions = new ConstraintDescriptions(input, (constraint) -> {
String message = (String) constraint.getConfiguration().get("message");
if (message != null && !Pattern.compile("\\{(.*?)\\}").matcher(message).matches()) {
return message;
}
return fallback.resolveDescription(constraint);
});
}
public FieldDescriptor withPath(String path)
{
return fieldWithPath(path).attributes(key("constraints").value(StringUtils
.collectionToDelimitedString(constraintDescriptions
.descriptionsForProperty(path), ". ")));
}
}

Wicket 7 - AutoCompleted Text field - to have onSelect method

We would like to implement AutoCompleteTextField field, once user has selected the field from AutoComplete result, then system would auto populate on other text field, i have used the component AjaxFormComponentUpdatingBehavior (blur), however this will take effect on every text input from AutoCompleteTextField field, but if i change to AjaxFormComponentUpdatingBehavior (change), it doesnt work.
Below is the sample code:
AutoCompleteTextField<String> field_postcode = new AutoCompleteTextField<String>("field_postcode",
new PropertyModel<String>(getModelObject(), "wAdditionalInfo.postal"), autoCompleteRenderer) {
private static final long serialVersionUID = 1L;
#Override
protected Iterator<String> getChoices(String input) {
if (Strings.isEmpty(input)) {
List<String> emptyList = Collections.emptyList();
return emptyList.iterator();
}
List<String> choices = new ArrayList<String>();
List<Postcode> postcodeList = getProfileManager().findAllPostcodeByPostcode(input);
for (Postcode p : postcodeList) {
String postcode = p.getPostcode();
if (postcode.startsWith(input)) {
choices.add(p.getPostcode());
if (choices.size() == 10) {
break;
}
}
}
return choices.iterator();
}
};
field_postcode.setRequired(true);
field_postcode.add(new AjaxFormComponentUpdatingBehavior("blur"){
private static final long serialVersionUID=-1107858522700306810L;
#Override protected void onUpdate( AjaxRequestTarget target){
Postcode postcode = getProfileManager().findPostcodeByPostcode(field_postcode.getInput());
if (postcode != null) {
City city = postcode.getCity();
State state = city.getState();
field_city.setModelObject(city.getCity());
ddl_state.setModelObject(state);
if (isDisplayTip) {
//isDisplayTip true mean is from widrawal webform
isReadonly = true;
} else {
field_city.setEnabled(false);
}
ddl_state.setEnabled(false);
} else {
if (isDisplayTip) {
isReadonly = false;
} else {
field_city.setEnabled(true);
}
ddl_state.setEnabled(true);
}
target.add(field_city, ddl_state);
}
}
);
Is there any api from wicket to achieve this? We need to have something when user select the option from Auto complete, then it only onUpdate method of AjaxFormComponentUpdatingBehavior
According to https://github.com/apache/wicket/blob/cbc237159c4c6632b4f7db893c28ab39d1b40ed4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js#L620 it should trigger change event on the HTMLInputElement and thus notify you on the server side.
Use the browser debugger to see whether https://github.com/apache/wicket/blob/cbc237159c4c6632b4f7db893c28ab39d1b40ed4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js#L453 is executed and whether it leads to an Ajax call with the value in the parameters.

How to get down to StringLiterals with Eclipse AST?

I need to create an Eclipse plugin that displays a tooltip when I hover the mouse over a String literal.
But only if that String literal is the first parameter of a special method.
Here is the Test.java file I use to test my plugin:
package test;
public class Test {
public static void main(String[] args) {
String hello = "Hello";
String world = Translator.get("Test.worldLabel");
System.out.println(hello + " " + world);
}
}
I created a class implementing IJavaEditorTextHover and I need to compile the currently edited Java file to compute if the cursor is hovering a String that needs to be translated or not.
Hovering "Hello" will do nothing.
Hovering "Test.worldLabel" will display my tooltip because that literal is included inside a Translator.get() method call.
At first I used this (170 is inside "Test.worldLabel"):
ITypeRoot typeRoot = (ITypeRoot)
JavaUI.getEditorInputJavaElement(editorPart.getEditorInput());
JavaElement foundElement = (JavaElement) typeRoot.getElementAt(170);
But the foundElement contains the whole main() method: it is not fine-grained enough.
Then, the correct way is, I think:
private static ASTNode parse(ICompilationUnit unit, int position) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(unit);
parser.setResolveBindings(true);
parser.setIgnoreMethodBodies(false);
// TODO Future optimisation: parser.setFocalPosition(position);
return parser.createAST((IProgressMonitor) null); // parse
}
And in my IJavaEditorTextHover.getHoverInfo(...) implementation:
ICompilationUnit compilationUnit = (ICompilationUnit)
JavaUI.getEditorInputJavaElement(editor.getEditorInput())
int position = 170/*hoverRegion.getOffset()*/;
ASTNode ast = parse(compilationUnit, position);
And now, here is my question:
How, from this ast node, do I get the ASTNode reprensenting the StringLiteral at position 170 in the source code (the "Test.worldLabel" String)?
Bonus question: did I choose the right solution? On a performance basis.
Edit:
Well, here is a solution I found:
private StringLiteral findStringLiteralAtPosition(final ASTNode parent, final int position) {
final List<StringLiteral> stringLiterals = new ArrayList<StringLiteral>();
parent.accept(new ASTVisitor() {
#Override
public boolean visit(StringLiteral stringLiteral) {
int start = stringLiteral.getStartPosition();
int end = start + stringLiteral.getLength();
if (start <= position && position <= end) {
stringLiterals.add(stringLiteral);
}
return super.visit(stringLiteral);
}
});
return (stringLiterals.size() > 0 ? stringLiterals.get(0) : null);
}
Does it seam OK?
Or is it an easier way or a more performant one?
One solution will be not using the offset logic at all.
You can generalise the solution by using a node parent check.
Here is a sample code:
public boolean visit(StringLiteral stringLiteral) {
// Check if parent is a method inovacation.
if (stringLiteral.getParent().getNodeType() == ASTNode.METHOD_INVOCATION) {
// get the parent method inovacation.
MethodInvocation miNode = (MethodInvocation) stringLiteral.getParent();
//To do: null and empty check on argument list.
// Check if is the special method and this is the 1st argument
if (miNode.getName().toString().equals("SpecialMethod")
&& miNode.arguments().get(0).toString().equals(stringLiteral.toString())) {
System.out.println("Found it : " + stringLiteral.toString());
}
}
return true;
}

GWT CellTable, disable ButtonCell when clicked

I'm pretty new to GWT, but I've been making pretty fast progress until now.
I have a cell table, most of which is read only data returned from an RPC.
I have two columns in the cell table that the user can interact with. One is a TextInputCell, one is a ButtonCell.
When the user clicks the ButtonCell, i want to send the value in the TextInputCell for that row to an RPC.
I have all this working.
The part I cannot get to work is that when the button (ButtonCell) is clicked, I want to disable the button in that row until the RPC returns, and then re-enable it. I also want to clear the text in the input cell for that row when the RPC returns.
I cannot figure out how to get handles to the actual ButtonCell object that was clicked or the TextInputCell to monkey with them.
Any help appreciated.
bq
The problem is that there's no object for the button that was clicked. Your ButtonCell creates HTML that renders buttons - every button in the whole column was written by the same button cell, but there's no java object associated with them.
To disable the button directly, you'll have to first create a handle to it. You could do this by rendering an id in the html your ButtonCell creates, and then getting the element by id from the DOM.
What I do in a similar case is just re-render the entire table when there's a state change. It doesn't take that long, and you don't need to store any references (the whole reason you're using CellTable instead of Grid anyway). When you know your button should be disabled, you just render it disabled.
Both of these suggestions would require you to subclass your Cell objects so that you can do some custom rendering. It's not very difficult, but wrapping your head around the order of operations can be confusing. Good luck!
PS: If you just want to disable the button (and not empty the text field), I think onBrowserEvent gives you a handle to the Element that was clicked - you might be able to use that to disable it.
I have gone through this problem, but eventually I solved it.
check this code
package com.ex7.client;
import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
public class CWButton extends ButtonCell {
private int row = -1;
private String alternativevalue;
private String exTitle = "";
private String value;
private String title = "";
public CWButton( ) {
super();
}
#Override
public void render(com.google.gwt.cell.client.Cell.Context context,
String src, SafeHtmlBuilder sb) {
if (row == -1) {
sb.appendHtmlConstant("<button title='" + title + "' >" +value+"</button>");
return;
}
if (row != context.getIndex()) {
sb.appendHtmlConstant("<Button disabled='disabled' title='" + title + "' >"+ value+"</button>");
} else {
sb.appendHtmlConstant("<button title='" + exTitle + "' >"+ alternativevalue+"</button>");
}
}
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, String value, NativeEvent event,
ValueUpdater<String> valueUpdater) {
if (row == -1 || row == context.getIndex()) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
return;
}
}
public void setTitle(String title) {
this.title = title;
}
public int getRow() {
return row;
}
public String getExTitle() {
return exTitle;
}
public void setExTitle(String exTitle) {
this.exTitle = exTitle;
}
public void setRow(int row) {
this.row = row;
}
public String getAlternativeValue() {
return alternativevalue;
}
public void setAlternativeValue(String alternativeValue) {
this.alternativevalue = alternativeValue;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

How to apply like search on GWT cell table?

I am using GWT 2.3.I which I am using GWT cell table.
Here below is the code for my cell table:
public class FormGrid extends SuperGrid {
List<Form> formList;
#Override
public void setColumns(CellTable table) {
TextColumn<Form> nameColumn = new TextColumn<Form>() {
#Override
public String getValue(Form object) {
return object.getName();
}
};
table.addColumn(nameColumn, "Name");
}
#Override
public void setData() {
if (formList != null && formList.size() > 0) {
AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() {
#Override
protected void onRangeChanged(HasData<Form> display) {
int start = display.getVisibleRange().getStart();
int end = start + display.getVisibleRange().getLength();
end = end >= formList.size() ? formList.size() : end;
List<Form> sub = formList.subList(start, end);
updateRowData(start, sub);
}
};
provider.addDataDisplay(getTable());
provider.updateRowCount(formList.size(), true);
}
}
public List<Form> getFormList() {
return formList;
}
public void setFormList(List<Form> formList) {
this.formList = formList;
}
}
In this my set column and set data will be called fro super class flow.This cell table is working fine.
Now I want to put a filter type facility (like search) in this cell table.It should be like, there is a texbox above the cell table and what ever written in that text box, it should fire a like query to all form name for that text box value.
for example I have 1000 form in the grid.Now if user writes 'app' in some filter textbox above the cell table the all the form which have 'app' in there name will be filtered and grid has only those forms only.
This is the first case:
Another case is I am only render one column in grid name.I have two more properties in form (description,tag).But I am not rendering them.now for filter if user writes 'app' in filter box then it should make a query to all three (name, description, and tag) and should return if 'app' matched to any of three.
I am not getting how to apply filter in cell table.
Please help me out.Thanks in advance.
You can find an implementation in the expenses sample.
Here is a short summary of the steps
1.) Create a Textbox and a SearchButton.
2.) add a clickHandler to the SearchButton (You can also add KeyUpHandler to the Textbox alternatively)
searchButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
search();
}
});
3.) In the search function retrieve the searchString and store it.
private void search() {
searchString = searchBox.getText();
setData();
}
4.) modify your setdata() function to take searchString into account
#Override
public void setData() {
if (formList != null && formList.size() > 0) {
AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() {
#Override
protected void onRangeChanged(HasData<Form> display) {
int start = display.getVisibleRange().getStart();
int end = start + display.getVisibleRange().getLength();
//new function if searchString is specified take into account
List<Form> sub = getSubList(start,end);
end = end >= sub.size() ? sub.size() : end;
updateRowData(sub.subList(start, end);, sub);
}
};
provider.addDataDisplay(getTable());
provider.updateRowCount(formList.size(), true);
}
}
private List<Form> getSubList(int start, int end) {
List<Form> filtered_list = null;
if (searchString != null) {
filtered_list= new ArrayList<Form>();
for (Form form : formList) {
if (form.getName().equals(searchString) || form.getTag().equals(searchString) || form.getDescription().equals(searchString))
filtered_list.add(form);
}
}
else
filtered_list = formList;
return filtered_list;
}
can propose another solution what can be used quite easy multiple times.
Idea is to create custom provider for your celltable.
GWT celltable filtering
Video in this post shows it in action.
Here is the part of code of custom list data provider which u have to implement.
#Override
protected void updateRowData(HasData display, int start, List values) {
if (!hasFilter() || filter == null) { // we don't need to filter, so call base class
super.updateRowData(display, start, values);
} else {
int end = start + values.size();
Range range = display.getVisibleRange();
int curStart = range.getStart();
int curLength = range.getLength();
int curEnd = curStart + curLength;
if (start == curStart || (curStart < end && curEnd > start)) {
int realStart = curStart < start ? start : curStart;
int realEnd = curEnd > end ? end : curEnd;
int realLength = realEnd - realStart;
List<t> resulted = new ArrayList<t>(realLength);
for (int i = realStart - start; i < realStart - start + realLength; i++) {
if (filter.isValid((T) values.get(i), getFilter())) {
resulted.add((T) values.get(i));
}
}
display.setRowData(realStart, resulted);
display.setRowCount(resulted.size());
}
}
}