1package im.conversations.android.xmpp.model.data;
2
3import com.google.common.base.CaseFormat;
4import com.google.common.collect.Collections2;
5import com.google.common.collect.Iterables;
6import im.conversations.android.annotation.XmlElement;
7import im.conversations.android.xmpp.model.Extension;
8import java.util.Collection;
9import java.util.Map;
10
11@XmlElement(name = "x")
12public class Data extends Extension {
13
14 private static final String FORM_TYPE = "FORM_TYPE";
15 private static final String FIELD_TYPE_HIDDEN = "hidden";
16 private static final String FORM_TYPE_SUBMIT = "submit";
17
18 public Data() {
19 super(Data.class);
20 }
21
22 public String getFormType() {
23 final var fields = this.getExtensions(Field.class);
24 final var formTypeField = Iterables.find(fields, f -> FORM_TYPE.equals(f.getFieldName()));
25 return Iterables.getFirst(formTypeField.getValues(), null);
26 }
27
28 public Collection<Field> getFields() {
29 return Collections2.filter(
30 this.getExtensions(Field.class), f -> !FORM_TYPE.equals(f.getFieldName()));
31 }
32
33 public Field getFieldByName(final String name) {
34 return Iterables.find(getFields(), f -> name.equals(f.getFieldName()), null);
35 }
36
37 public String getValue(final String name) {
38 final var field = getFieldByName(name);
39 return field == null ? null : field.getValue();
40 }
41
42 private void addField(final String name, final Object value) {
43 addField(name, value, null);
44 }
45
46 private void addField(final String name, final Object value, final String type) {
47 if (value == null) {
48 throw new IllegalArgumentException("Null values are not supported on data fields");
49 }
50 final var field = this.addExtension(new Field());
51 field.setFieldName(name);
52 if (type != null) {
53 field.setType(type);
54 }
55 if (value instanceof Collection) {
56 for (final Object subValue : (Collection<?>) value) {
57 if (subValue instanceof String) {
58 final var valueExtension = field.addExtension(new Value());
59 valueExtension.setContent((String) subValue);
60 } else {
61 throw new IllegalArgumentException(
62 String.format(
63 "%s is not a supported field value",
64 subValue.getClass().getSimpleName()));
65 }
66 }
67 } else {
68 final var valueExtension = field.addExtension(new Value());
69 if (value instanceof String) {
70 valueExtension.setContent((String) value);
71 } else if (value instanceof Enum<?> e) {
72 valueExtension.setContent(
73 CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, e.toString()));
74 } else if (value instanceof Integer) {
75 valueExtension.setContent(String.valueOf(value));
76 } else if (value instanceof Boolean) {
77 valueExtension.setContent(Boolean.TRUE.equals(value) ? "1" : "0");
78 } else {
79 throw new IllegalArgumentException(
80 String.format(
81 "%s is not a supported field value",
82 value.getClass().getSimpleName()));
83 }
84 }
85 }
86
87 private void setFormType(final String formType) {
88 this.addField(FORM_TYPE, formType, FIELD_TYPE_HIDDEN);
89 }
90
91 public static Data of(final String formType, final Map<String, Object> values) {
92 final var data = new Data();
93 data.setType(FORM_TYPE_SUBMIT);
94 data.setFormType(formType);
95 for (final Map.Entry<String, Object> entry : values.entrySet()) {
96 data.addField(entry.getKey(), entry.getValue());
97 }
98 return data;
99 }
100
101 public Data submit(final Map<String, Object> values) {
102 final String formType = this.getFormType();
103 final var submit = new Data();
104 submit.setType(FORM_TYPE_SUBMIT);
105 if (formType != null) {
106 submit.setFormType(formType);
107 }
108 for (final Field existingField : this.getFields()) {
109 final var fieldName = existingField.getFieldName();
110 final Object submittedValue = values.get(fieldName);
111 if (submittedValue != null) {
112 submit.addField(fieldName, submittedValue);
113 } else {
114 submit.addField(fieldName, existingField.getValues());
115 }
116 }
117 return submit;
118 }
119
120 private void setType(final String type) {
121 this.setAttribute("type", type);
122 }
123}