map two boolean fields of an IModel to a RadioChoice in Wicket - java

I have a User object with two boolean fields:
//User Bean
public class Users {
private Boolean showContactData;
private Boolean showContactDataToContacts;
// Getters + Setters
}
I would like to show that as a RadioChoice in the UI using Apache Wicket.
Snippet from the HTML part:
<input type="radio" wicket:id="community_settings"/>
Java Form with radio in Wicket
public class UserForm extends Form<Users> {
public UserForm(String id, Users user) {
super(id, new CompoundPropertyModel<Users>(user));
RadioChoice rChoice = new RadioChoice<Long>("community_settings", choices, renderer);
add(rChoice );
}
}
My issue is now that I of course have no attribute community_settings in the Users Object.
I simply want to map those two boolean values to a radio choice in the UI.
How could I do that in Wicket?
Thanks!
Sebastian

You need a model to map the data:
RadioChoice<Long> rChoice = new RadioChoice<Long>("community_settings", new IModel<Long>() {
public Long getObject() {
if (user.getShowContactData() && user.getShowContactDataToContacts()) {
return 1L;
}
// ...
}
public void setObject(Long choice) {
if (choice == 1) {
user.setShowContactData(true);
user.setShowContactDataToContacts(true);
} else if (choice == 2) {
// ...
}
}
public void detach() {
}
}, choices);
BTW, personally I would use an Enum, so there's no special mapping at all. It seems like you are doing things a bit too "low level" and so the wicket model stuff will feel really cumbersome. Try to use objects instead of primitive types if it's appropriate.

Related

Efficient Java construct for Arraylist Processing and calling different methods

Please, Is there an elegant and efficient way of doing the following in Post Java 8 i.e.
1. Looping through an arraylist
2. Reading the object
3. Calling different methods of potentially different objects using the values contained in the arraylist items
I did look at streams, switch statement, but it was much messy than my if-else.
Any help will be appreciated. Just looking for continuous improvements
List<JAXBElement<? extends Serializable>> bodyElements = eventRequestBodyTypeSrc.getContent();
for(JAXBElement element: bodyElements){
if(element.getName().getLocalPart().equalsIgnoreCase("A")){
methodA(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("B")){
methodB(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("C")){
methodC(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("D")){
methodD(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("E")){
methodE(element.getValue());
}else{
}
}
I think you have a bit of an XY Problem going on. I would refactor this at a higher level to encapsulate the strings and their related actions. Here's a rough concept using an enum:
enum Action {
A("a") {
#Override
void doSomething(Serializable value) {
// ...
}
},
B("b") {
#Override
void doSomething(Serializable value) {
// ...
}
};
private static final Map<String, Action> actionsByName = Arrays.stream(values())
.collect(Collectors.toMap(a -> a.name, a -> a));
private final String name;
Action(String name) {
this.name = name;
}
abstract void doSomething(Serializable value);
public static void processElement(JAXBElement<? extends Serializable> element) {
Action action = actionsByName.get(element.getName().getLocalPart().toLowerCase());
if (action != null) {
action.doSomething(element.getValue());
} else {
// ...
}
}
}
As a lean solution I would gather all mappings first as follows:
Map<String, Consumer<Serializable>> dispatchers = new HashMap<>();
dispatchers.put("A", this::methodA);
// etc.
...and dispatch the elements like that:
Consumer<Serializable> dispatcher = dispatchers.get(element.getName().getLocalPart().toUpperCase(Locale.US));
if (dispatcher != null) {
dispatcher.accept(element.getValue());
}

JavaFx: Populate ComboBox with different enums depending on another ComboBox

I have two ComboBoxes:
final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<>();
final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>();
Depending on the value chosen in comboBoxMainCategory, the comboBoxSubCategory should be populated with the corresponding enum.
public enum MainCategory { // extra enum class
EUROPE("Europe"),
USA("USA");
}
public enum SubCategoryEurope { // extra enum class
GERMANY("Germany"),
FRANCE("France");
}
public enum SubCategoryUSA {
COLORADO("Colorado"),
CALIFORNIA("California");
}
If "Europe" is chosen for comboBoxMainCategory, comboBoxSubCategory should be populated with SubCategoryEurope. If "USA", with SubCategoryUSA.
How do you achieve this?
Here's my code:
final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<();
final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>();
comboBoxMainCategory.valueProperty().addListener((obs, oldValue,
newValue) ->
{
if (newValue == null) { // newValue: Europe || USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
} else if (newValue.equals(MainCategory.EUROPE)) {
comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryEurope.values()));
comboBoxSubCategory.setDisable(false);
} else {
comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryUSA.values()));
comboBoxSubCategory.setDisable(false);}
});
Problem is, because comboBoxSubCategory is "SubCategory", there is a type error if it is populated with 'SubCategoryEurope' or 'SubCategoryUSA'.
What is the best way to solve this? Sorry if it's a silly question, I'm new to JavaFx.
Thanks a lot!
I wouldn't use enums at all, since this doesn't allow for data manipulation without recompiling. If you insist on using enums though, you need to use Object or a interface implemented with both subcategory enum types as parameter type for comboBoxSubCategory:
comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue == null) { // newValue: Europe || USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
} else {
comboBoxSubCategory.setDisable(false);
List<? extends Object> list;
switch (newValue) {
case EUROPE:
list = Arrays.asList(SubCategoryEurope.values());
break;
default:
list = Arrays.asList(SubCategoryUSA.values());
break;
}
comboBoxSubCategory.getItems().setAll(list);
}
});
The better approach would be using a Map<String, List<String>> to store the data:
Map<String, List<String>> data = new HashMap<>();
data.put("EUROPE", Arrays.asList("GERMANY", "FRANCE"));
data.put("USA", Arrays.asList("COLORADO", "CALIFORNIA"));
comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
List<String> list = data.get(newValue);
if (list != null) {
comboBoxSubCategory.setDisable(false);
comboBoxSubCategory.getItems().setAll(list);
} else {
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
}
});
Just for fun (and to flesh out my comments): a more versatile approach than those in the other answers is to move away the focus of interest from the concrete nature of the backing data to a more general solution of the use-case at hand. The drawback of letting the UI implement special cases is always the same - you have to do it over and over again for each special UI and each special data type. The way out is always the same, too: implement a Model that takes over the general aspect and re-use that in concrete UI/data contexts.
The general aspects here are:
there's list of items with each having a list of dependent objects (same or different type)
this (let's call it root) list of items is shown in a control
from root list, a single item can be chosen (aka: selected)
another control should show the dependents of the root
The general approach is to have a Model that
manages list of items
has the notion of one of those items as selected (or current or active or ..)
manages a list of dependent items that always is the dependent list of the selected item
its state (root items, current item, dependent items) is exposed as properties
The advantages of such a Model are
can be formally and rigorouly tested, so using code can rely on its proper functioning
it's re-usable for any data context
it's re-usable for many controls
usage is pretty simple by binding
In the example below, the Model is named RelationModel which expects root items of type RelationProvider (which allows access to a list of dependents, it's one option, could just as well use f.i. a Function to build the dependents). It is used once with a plain Map of String/list and once with enums of Continents/Countries, each very simple to implement. Note that the resulting UI is blissfully unaware of the nature of the data, implemented solely against the model.
Naturally, not production grade, in particular, not formally tested and the model with just the barest functionality :)
public class CombosWithCategories extends Application {
public interface RelationProvider<T> {
default ObservableList<T> getRelations() {
return emptyObservableList();
};
}
/**
* A model that manages a list of RelationProviders and has the notion
* of a current relationProvider with relations (it's a kind-of selectionModel).
*
* <T> the type of elements in the list of relations
*/
public static class RelationModel<T> {
/**
* all relationProviders managed by this model
*/
private ListProperty<RelationProvider<T>> relationProviders;
/**
* The owner of the relations. Must be contained in the providers managed
* by this model.
*/
private ObjectProperty<RelationProvider<T>> relationProvider;
private ListProperty<T> relations;
public RelationModel() {
initProperties();
}
/**
* The RelationProviders managed by the model.
*/
public ListProperty<RelationProvider<T>> relationProvidersProperty() {
return relationProviders;
}
/**
* The RelationProvider that manages the current relations.
*/
public ObjectProperty<RelationProvider<T>> relationProviderProperty() {
return relationProvider;
}
public RelationProvider<T> getRelationProvider() {
return relationProviderProperty().get();
}
public ListProperty<T> relations() {
return relations;
}
/**
* Callback from invalidation of current relationProvider.
* Implemented to update relations.
*/
protected void relationProviderInvalidated() {
RelationProvider<T> value = getRelationProvider();
relations().set(value != null ? value.getRelations() : emptyObservableList());
}
/**
* Creates and wires all properties.
*/
private void initProperties() {
relationProviders = new SimpleListProperty<>(this, "relationProviders", observableArrayList());
relationProvider = new SimpleObjectProperty<>(this, "relationProvider") {
#Override
protected void invalidated() {
// todo: don't accept providers that are not in the list
relationProviderInvalidated();
}
};
relations = new SimpleListProperty<>(this, "relations");
relationProviderInvalidated();
}
}
/**
* Implement the ui against a RelationModel. Here we create
* the same UI with a model backed by enums or a Map, respectively
*/
private Parent createContent() {
TabPane tabPane = new TabPane(
new Tab("Enums", createRelationUI(createEnumRelationModel())),
new Tab("Manual map", createRelationUI(createMapRelationModel()))
);
return new BorderPane(tabPane);
}
/**
* Common factory for UI: creates and returns a Parent that
* contains two combo's configured to use the model.
*/
protected <T> Parent createRelationUI(RelationModel<T> model) {
ComboBox<RelationProvider<T>> providers = new ComboBox<>();
providers.itemsProperty().bind(model.relationProvidersProperty());
providers.valueProperty().bindBidirectional(model.relationProviderProperty());
ComboBox<T> relations = new ComboBox<>();
relations.itemsProperty().bind(model.relations());
relations.valueProperty().addListener((src, ov, nv) -> {
LOG.info("relation changed: " + nv);
});
return new VBox(10, providers, relations);
}
// ------------- manual with maps
/**
* On-the-fly creation of a RelationModel using a backing map.
*/
protected RelationModel<String> createMapRelationModel() {
RelationModel<String> model = new RelationModel<>();
Map<String, ObservableList<String>> data = new HashMap<>();
data.put("EUROPE", observableArrayList("GERMANY", "FRANCE"));
data.put("AMERICA", observableArrayList("MEXICO", "USA"));
for (String key: data.keySet()) {
model.relationProvidersProperty().add(new RelationProvider<String>() {
#Override
public ObservableList<String> getRelations() {
return data.get(key);
}
#Override
public String toString() {
return key;
}
});
}
return model;
}
//-------------------- enum
/**
* RelationModel using Enums.
*/
protected RelationModel<Object> createEnumRelationModel() {
RelationModel<Object> model = new RelationModel<Object>();
model.relationProvidersProperty().setAll(Continent.values());
return model;
}
public enum EuropeanCountry {
FRANCE, GERMANY;
}
public enum AmericanCountry {
MEXICO, CANADA, USA;
}
public enum Continent implements RelationProvider<Object> {
AMERICA(AmericanCountry.values()),
EUROPE(EuropeanCountry.values())
;
ObservableList<Object> subs;
private Continent(Object[] subs) {
this.subs = FXCollections.observableArrayList(subs);
}
#Override
public ObservableList<Object> getRelations() {
return FXCollections.unmodifiableObservableList(subs);
}
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(CombosWithCategories.class.getName());
}
Create a generic placeholder interface
public interface EnumPlaceHolder<E extends Enum<?>> {
public abstract String getDisplayValue();
public abstract E getEnum();
}
Create an implementation for all your enums. For example
public class EuropePlaceholder implements EnumPlaceHolder<Europe> {
private final Europe value;
public EuropePlaceholder(Europe pValue){
value = pValue;
}
#Override
public String getDisplayValue() {
// here you create a user-friendly version of your enum for display
return value.toString();
}
#Override
public Europe getEnum() {
return value;
}
}
Then change the type of your ComboBox<Enum> to ComboBox<EnumPlaceholder<?>> and you can add any of your implemented EnumPlaceholders to it. When retrieving the selected item you can check which one is contained via instance check
EnumPlaceholder<?> selectedItem = ...;
if(selectedItem instanceof EuropePlaceholder){
Europe selectedEuropeEnum = (Europe) selectedItem.getEnum();
} else if(....){
// check with else if for your other enums
}
And to display your enum in your combobox you call the getDisplayValue() of the EnumPlaceholder and show the returned String in your cell :)
EDIT
Tho in general i have to agree with fabians answer. You shouldn't use enums for a construct like this. Rather use a Map<> or a List<> with appropriate content and structure.

Issue with posting arraylist with indexed properties from jsp to action class

I'm having this issue where I have dynamically generated values in hidden inputs like so:
<div id="items-div" class="selection-div">
<input name="selectedItem[0].articleName" id="selectedItem" type="hidden" value="cereal"></input>
<input name="selectedItem[0].quantity" id="selectedItem" type="hidden" value="2"></input>
<input name="selectedItem[1].articleName" id="selectedItem" type="hidden" value="yogurt"></input>
<input name="selectedItem[1].quantity" id="selectedItem" type="hidden" value="10"></input>
</div>
I append these to the items-div using jquery depending on what the user chooses on-screen, each of those is an Item.
public class Item {
private String articleName = "";
private int quantity = 0;
public String getArticleName() {
return articleName;
}
public void setArticleName(String articleName) {
this.articleName = articleName;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
And this is the form class which has an ArrayList of these items
public class CreateArticleForm extends ActionForm {
private ArrayList<Item> selectedList = new ArrayList<Item>();
public Item getSelectedItem(int index) {
if (selectedList == null) {
selectedList = new ArrayList<Item>();
}
while (index >= selectedList.size()) {
selectedList.add(new Item());
}
return (Item) selectedList.get(index);
}
public ArrayList<Item> getSelectedList() {
return selectedList;
}
public void setSelectedList(ArrayList<Item> selectedList) {
this.selectedList = selectedList;
}
}
And finally the action class
public class CreateArticleAction extends
Action {
public ActionForward executeAction(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response, User user)
throws Throwable {
CreateArticleForm articleForm = (CreateArticleForm) form;
//... do stuff
}
}
I would expect that on post all the server would round up all the selectedItem[X] and call the getSelectedItem to build up the arraylist and populate it then set the properties to each object but when I submit the page on debug, I see the selectedList arraylist comes up empty. I am using this logic based from a previous assignment where instead the information with indexed properties was instead generated when the page was first loaded and then printed with a logic:iterate tag and then each input had a indexed="true" property, but since in this case it is from what the user clicks on the page, I instead use jquery to fill out the "items-div" with what the user is choosing, but it should be the same no? Am I missing something? It ran previously just fine... Thank you for your time.
EDIT: The above works if I hard-code test hidden inputs in the jsp page, it submits those but not the ones dynamically created and appended with jquery.
$('.items-div').append($('<input/>').attr({
type : 'hidden', name: 'selectedItem['+index+'].articleName', id : 'selectedItem', value: objItem.articleName}));
I should've known since before, but apparently it's a security measure to only allow elements that had been originally rendered to be posted back, so the ones I hard-coded worked just fine, but any others appended -after- rendering won't be submitted so I will probably have to put the objects in an array of objects, JSONfiy them and send them as a string to the action class, using an existing hidden input.

Convert selected DropDownChoice to Model in CompoundPropertyModel

Is it possible, in a Wicket way, to convert the selected DropDownChoices value, when the form has a CompoundPropertyModel attached with a Model, which has another type for the specific attribute.
Simple example, because i guess my explanation isn't very accurate:
public enum MyChoices {
ONE(1),TWO(2),THREE(3);
// ... etc
}
public class MyEntityModel {
private int number;
private String text;
}
// the WebPages constructor:
public ChoicePage() {
IModel<MyEntityModel> model = new CompoundPropertyModel<>(new EntityModel());
Form<MyEntityModel> form = new Form<MyEntityModel>("form", model);
add(form);
form.add(new TextField<String>("text"));
form.add(new DropDownChoice<>("choices", Model.of(MyChoices.ONE),
Arrays.asList(MyChoices.values()))
}
When submitting the form with ONE selected, i want the model object to have the value 1.
I do know, that i could name the DropDownChoice component other than the MyEntityModel field and copy it's value into the model after submit. But that is not Wickets model approach, is it?
P.s.: I'm using Wicket 6.17.0
You'll have to do some conversion.
Either convert the list of choices:
form.add(new DropDownChoice<Integer>("number",
new AbstractReadOnlyModel<List<Integer>>() {
public List<Integer> getObject() {
return MyChoices.getAllAsInts();
}
}
);
or the selected choice:
form.add(new DropDownChoice<MyChoices>("number", Arrays.asList(MyChoices.values()) {
public IModel<?> initModel() {
final IModel<Integer> model = (IModel<Integer>)super.initModel();
return new IModel<MyChoice>() {
public MyChoice getObject() {
return MyChoice.fromInt(model.getObject());
}
public void setObject(MyChoice myChoice) {
model.setObject(myChoice.toInt());
}
};
}
);

Trimming Struts2 textfield string input

What is the best way to trim this string/where is the best place to put the trim code?
Say I have the following textfield in my jsp:
<s:textfield label="First Name" name="person.firstname"/>
The action class:
public class BaseAction extends ActionSupport implements ServletRequestAware, SessionAware {
private Person person;
// Getters, setters and action logic
}
The bean:
public class Person implements Serializable {
private String lastname;
private String firstname;
// Getters and setters
}
I can change the default setting in the bean but this seems like a hack:
public void setFirstname(String firstname) {
this.firstname = firstname.trim();
}
EDIT: I did also see this question: struts2 trim all string obtained from forms where it's also suggested by some that the "correct" method is to use an interceptor.
Why is an interceptor the "correct" way? What is so wrong about changing the bean's setters?
It can be done with Struts2 converters.
public class TrimmingStringConverter extends StrutsTypeConverter {
public Object convertFromString(Map ctx, String[] values, Class arg2) {
if (values != null && values.length > 0) {
return values[0].trim();
}
return null;
}
public String convertToString(Map ctx, Object o) {
if (o != null) {
return o.toString();
}
else {
return null;
}
}
public Object convertValue(Map context, Object o, Class toClass)
{
if (o == null) {
return null;
} else if (toClass == java.lang.String.class) {
if (o instanceof String[]) {
String[] os = (String[]) o;
if (os.length > 0) {
return os[0].trim();
}
}
return o.toString().trim();
}
return super.convertValue(context, o, toClass);
}
}
It must be registered in xwork-conversion.properties:
java.lang.String=es.jogaco.webapp.TrimmingStringConverter
This will be applied to all user input.
It will work if you have the default struts2 interceptors. Quoted from struts2 doc:
By default, the conversion interceptor is included in struts-default.xml in the default stack
Plus I have it working in my struts2 app.
Short answer is Not by default, there is no build in mechanism to do this and you either need to do it in your action class or some-kind of java-script will do that for you.
Other possible way is to create an interceptor to do this with option to excludes or something like on similar trek.
I believe Interceptor is a good way to do this,its better to have such interceptor comes with S2.

Categories

Resources