Skip to content

Commit dee0666

Browse files
committed
💚 NPE when adding fields without @index
fix #53
1 parent b74bfa0 commit dee0666

File tree

2 files changed

+105
-135
lines changed

2 files changed

+105
-135
lines changed

demo/src/main/java/io/asfjava/ui/demo/screen/DemoForm.java

+30-123
Original file line numberDiff line numberDiff line change
@@ -2,148 +2,55 @@
22

33
import java.io.Serializable;
44

5-
import io.asfjava.ui.core.form.CheckBox;
6-
import io.asfjava.ui.core.form.ComboBox;
7-
import io.asfjava.ui.core.form.Number;
8-
import io.asfjava.ui.core.form.Password;
9-
import io.asfjava.ui.core.form.RadioBox;
5+
import io.asfjava.ui.core.form.Index;
106
import io.asfjava.ui.core.form.Tab;
11-
import io.asfjava.ui.core.form.TextArea;
127
import io.asfjava.ui.core.form.TextField;
138

149
public class DemoForm implements Serializable {
1510

16-
17-
@TextField(title = "Pesonal Website",fieldAddonLeft="http://", description = "This is TextField with fieldAddonLeft")
18-
private String webSite;
19-
20-
@TextField(title = "Your Github Mail",fieldAddonRight="@github.com", description = "This is TextField with fieldAddonRight")
21-
private String gitHub;
22-
23-
// @Tab(title = "Contact", index = 2)
24-
@Password(title = "Password", placeHolder = "Please set you password", description = "This is password")
25-
private String password;
11+
@Index(1)
12+
@TextField(title = "index 1")
13+
private String index1;
2614

27-
@Tab(title = "Info", index = 1)
28-
@TextField(title = "First Name", placeHolder = "Your first name", description = "This is a description for your first name field")
29-
private String firstName;
15+
@Tab(index = 2, title = "Tab2")
16+
@Index(0)
17+
@TextField(title = "index 2")
18+
private String index2;
3019

31-
// @Tab(title = "Info", index = 1)
32-
@TextField(title = "Last Name", placeHolder = "Your last name")
33-
private String lastName;
20+
@Tab(index = 3, title = "Tab1")
21+
@Index(2)
22+
@TextField(title = "Tab1 f3 index 2")
23+
private String index3;
3424

35-
@Tab(title = "Contact", index = 2)
36-
@TextField(title = "eMail", placeHolder = "Your email", pattern = "^\\S+@\\S+$", validationMessage = "Your mail must be in this format [email protected]", description = "This is Text Field with pattern and validation message")
37-
private String email;
25+
@Tab(index = 3, title = "Tab1")
26+
@Index(1)
27+
@TextField(title = "Tab1 f4 index 1")
28+
private String index4;
3829

39-
@Tab(title = "Additional Info", index = 3)
40-
@Number(title = "Number of children", placeHolder = "Number of children", description = "This is a number")
41-
private Integer number;
30+
@Tab(index = 2, title = "Tab2")
31+
@TextField(title = "Tab2 f5 index 0")
32+
private String index5;
4233

34+
private static final long serialVersionUID = -5073515619469444978L;
4335

44-
@Tab(title = "Info", index = 1)
45-
@ComboBox(title = "Gender", titleMap = GenderTitleMap.class)
46-
private String gender;
47-
48-
// @Tab(title = "Additional Info", index = 3)
49-
@ComboBox(title = "Currency", values = { "euro", "dollar" })
50-
private String currency;
51-
52-
@Tab(title = "Additional Info", index = 3)
53-
@RadioBox(title = "Civil State", titleMap = CivilStateTitelsMap.class)
54-
private String civilState;
55-
56-
// @Tab(title = "Contact", index = 2)
57-
@TextArea(title = "Address", placeHolder = "Fill your address please", description = "This is textarea")
58-
private String address;
59-
60-
@Tab(title = "Additional Info", index = 3)
61-
@CheckBox(title = "Color", values = { "red", "bleu", "green" }, defaultvalue = "red")
62-
private String color;
63-
64-
public String getFirstName() {
65-
return firstName;
66-
}
67-
68-
public void setFirstName(String firstName) {
69-
this.firstName = firstName;
70-
}
71-
72-
public String getLastName() {
73-
return lastName;
74-
}
75-
76-
public void setEmail(String eMail) {
77-
this.email = eMail;
78-
}
79-
80-
public String getEmail() {
81-
return email;
82-
}
83-
84-
public void setGitHub(String github) {
85-
this.gitHub = github;
86-
}
87-
88-
public String getGitHub() {
89-
return gitHub;
90-
}
91-
92-
public void setWebSite(String website) {
93-
this.webSite = website;
94-
}
95-
96-
public String getWebSite() {
97-
return webSite;
98-
}
99-
100-
public void setLastName(String lastName) {
101-
this.lastName = lastName;
102-
}
103-
104-
public Integer getNumber() {
105-
return number;
106-
}
107-
108-
public void setNumber(Integer number) {
109-
this.number = number;
110-
}
111-
112-
public String getPassword() {
113-
return password;
114-
}
115-
116-
public void setPassword(String password) {
117-
this.password = password;
118-
}
119-
120-
public String getGender() {
121-
return gender;
122-
}
123-
124-
public String getAddress() {
125-
return address;
126-
}
127-
128-
public void setAddress(String address) {
129-
this.address = address;
36+
public String getIndex1() {
37+
return index1;
13038
}
13139

132-
public String getCivilState() {
133-
return civilState;
40+
public String getIndex2() {
41+
return index2;
13442
}
13543

136-
public void setCivilState(String civilState) {
137-
this.civilState = civilState;
44+
public String getIndex4() {
45+
return index4;
13846
}
13947

140-
public String getCurrency() {
141-
return currency;
48+
public String getIndex5() {
49+
return index5;
14250
}
14351

144-
public String getColor() {
145-
return color;
52+
public String getIndex3() {
53+
return index3;
14654
}
14755

148-
private static final long serialVersionUID = -5073515619469444978L;
14956
}

src/main/java/io/asfjava/ui/core/schema/UiFormSchemaGenerator.java

+75-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import java.util.LinkedHashMap;
1111
import java.util.List;
1212
import java.util.Map;
13+
import java.util.Map.Entry;
1314
import java.util.function.Predicate;
15+
import java.util.stream.Collectors;
1416

1517
import com.fasterxml.jackson.databind.JsonMappingException;
1618
import com.fasterxml.jackson.databind.JsonNode;
@@ -35,32 +37,60 @@ public UiForm generate(Class<? extends Serializable> formDto) throws JsonMapping
3537

3638
JsonSchemaGenerator schemaGen = initSchemaGen(mapper);
3739
JsonSchema schema = generateSchema(formDto, schemaGen);
40+
3841
Map<Field, JsonNode> nodes = initFieldFormDefinition(mapper, declaredFields);
3942

43+
Map<Field, JsonNode> sortedNodes = reorderFieldsBasedOnIndex(nodes);
44+
4045
handlerGroupedFields();
4146

42-
ObjectNode tabbedFields = handleTabbedFields(mapper, declaredFields, nodes);
47+
ObjectNode tabbedFields = handleTabbedFields(mapper, declaredFields, sortedNodes);
4348

4449
ArrayNode formDefinition = mapper.createArrayNode();
4550
formDefinition.add(tabbedFields);
46-
nodes.entrySet().stream().forEach(nodesElement -> formDefinition.add(nodesElement.getValue()));
51+
sortedNodes.entrySet().stream().forEach(nodesElement -> formDefinition.add(nodesElement.getValue()));
4752

4853
return new UiForm(schema, formDefinition);
4954
}
5055

56+
private Map<Field, JsonNode> reorderFieldsBasedOnIndex(Map<Field, JsonNode> nodes) {
57+
58+
Comparator<? super Entry<Field, JsonNode>> tabIndexComparator = (entry1, entry2) -> {
59+
60+
Index field1Index = entry1.getKey().getAnnotation(Index.class);
61+
Index field2Index = entry2.getKey().getAnnotation(Index.class);
62+
63+
return Integer.compare((field1Index != null ? field1Index.value() : Integer.MAX_VALUE),
64+
field2Index != null ? field2Index.value() : Integer.MAX_VALUE);
65+
};
66+
67+
return nodes.entrySet().stream().sorted(tabIndexComparator).collect(Collectors.toMap(Map.Entry::getKey,
68+
Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
69+
70+
}
71+
5172
private void handlerGroupedFields() {
5273
// TODO Grouping fieldset must handle it
5374

5475
}
5576

5677
private ObjectNode handleTabbedFields(ObjectMapper mapper, Field[] declaredFields, Map<Field, JsonNode> nodes) {
5778
Predicate<? super Field> checkTabAnnotation = field -> field.isAnnotationPresent(Tab.class);
79+
5880
Comparator<? super Field> tabIndexComparator = (field1, field2) -> Integer
5981
.compare(field1.getAnnotation(Tab.class).index(), field2.getAnnotation(Tab.class).index());
6082

83+
Comparator<? super Field> fieldIndexComparator = (entry1, entry2) -> {
84+
Index field1Index = entry1.getAnnotation(Index.class);
85+
Index field2Index = entry2.getAnnotation(Index.class);
86+
87+
return Integer.compare((field1Index != null ? field1Index.value() : Integer.MAX_VALUE),
88+
field2Index != null ? field2Index.value() : Integer.MAX_VALUE);
89+
};
90+
6191
Map<String, List<JsonNode>> groupedFieldsByTab = new LinkedHashMap<>();
6292

63-
Arrays.stream(declaredFields).filter(checkTabAnnotation).sorted(tabIndexComparator)
93+
Arrays.stream(declaredFields).filter(checkTabAnnotation).sorted(fieldIndexComparator).sorted(tabIndexComparator)
6494
.forEach(field -> groupFieldsByTab(nodes, field, groupedFieldsByTab));
6595

6696
ArrayNode tabs = mapper.createArrayNode();
@@ -82,15 +112,10 @@ private ObjectNode handleTabbedFields(ObjectMapper mapper, Field[] declaredField
82112
}
83113

84114
private Map<Field, JsonNode> initFieldFormDefinition(ObjectMapper mapper, Field[] declaredFields) {
85-
Map<Field, JsonNode> nodes = new LinkedHashMap<>();
86-
87-
Predicate<? super Field> checkIndexAnnotation = field -> field.isAnnotationPresent(Index.class);
88-
Comparator<? super Field> fieldIndexComparator = (field1, field2) -> Integer
89-
.compare(field1.getAnnotation(Index.class).value(), field2.getAnnotation(Index.class).value());
90-
91-
Arrays.stream(declaredFields).filter(checkIndexAnnotation).sorted(fieldIndexComparator)
92-
.forEachOrdered(field -> buildFormDefinition(nodes, mapper, field));
93-
115+
Map<Field, JsonNode> nodes = new HashMap<>();
116+
117+
Arrays.stream(declaredFields).forEach(field -> buildFormDefinition(nodes, mapper, field));
118+
94119
return nodes;
95120
}
96121

@@ -139,4 +164,42 @@ public static UiFormSchemaGenerator get() {
139164
private UiFormSchemaGenerator() {
140165
}
141166

167+
public static void main(String[] argv) {
168+
169+
Map<String, Integer> unsortMap = new HashMap<>();
170+
unsortMap.put("z", 10);
171+
unsortMap.put("b", 5);
172+
unsortMap.put("a", 6);
173+
unsortMap.put("c", 20);
174+
unsortMap.put("d", 1);
175+
unsortMap.put("e", 7);
176+
unsortMap.put("y", 8);
177+
unsortMap.put("n", 99);
178+
unsortMap.put("g", 50);
179+
unsortMap.put("m", 2);
180+
unsortMap.put("f", 9);
181+
182+
System.out.println("Original...");
183+
System.out.println(unsortMap);
184+
185+
// sort by keys, a,b,c..., and return a new LinkedHashMap
186+
// toMap() will returns HashMap by default, we need LinkedHashMap to
187+
// keep the order.
188+
Map<String, Integer> result = unsortMap.entrySet().stream().sorted(Map.Entry.comparingByKey())
189+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue,
190+
LinkedHashMap::new));
191+
192+
// Not Recommend, but it works.
193+
// Alternative way to sort a Map by keys, and put it into the "result"
194+
// map
195+
Map<String, Integer> result2 = new LinkedHashMap<>();
196+
unsortMap.entrySet().stream().sorted(Map.Entry.comparingByKey())
197+
.forEachOrdered(x -> result2.put(x.getKey(), x.getValue()));
198+
199+
System.out.println("Sorted...");
200+
System.out.println(result);
201+
System.out.println(result2);
202+
203+
}
204+
142205
}

0 commit comments

Comments
 (0)