1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.form.field;
35
36 import info.magnolia.cms.i18n.I18nContentSupport;
37 import info.magnolia.objectfactory.ComponentProvider;
38 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
39 import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
40 import info.magnolia.ui.form.field.definition.FieldDefinition;
41 import info.magnolia.ui.form.field.factory.FieldFactory;
42 import info.magnolia.ui.form.field.factory.FieldFactoryFactory;
43 import info.magnolia.ui.vaadin.integration.ItemAdapter;
44 import info.magnolia.ui.vaadin.integration.NullItem;
45 import info.magnolia.ui.vaadin.server.ErrorMessageUtil;
46
47 import java.util.ArrayList;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Locale;
51
52 import org.apache.commons.lang3.StringUtils;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import com.vaadin.server.AbstractErrorMessage.ContentMode;
57 import com.vaadin.server.CompositeErrorMessage;
58 import com.vaadin.server.ErrorMessage;
59 import com.vaadin.server.UserError;
60 import com.vaadin.ui.AbstractOrderedLayout;
61 import com.vaadin.ui.Component;
62 import com.vaadin.ui.HasComponents;
63 import com.vaadin.v7.data.Item;
64 import com.vaadin.v7.data.Property;
65 import com.vaadin.v7.data.util.PropertysetItem;
66 import com.vaadin.v7.ui.AbstractField;
67 import com.vaadin.v7.ui.CustomField;
68 import com.vaadin.v7.ui.Field;
69
70
71
72
73
74
75
76
77
78
79
80 public abstract class AbstractCustomMultiField<D extends FieldDefinition, T> extends CustomField<T> {
81
82 private static final Logger log = LoggerFactory.getLogger(AbstractCustomMultiField.class);
83
84 protected final FieldFactoryFactory fieldFactoryFactory;
85
86
87 @Deprecated
88 protected final I18nContentSupport i18nContentSupport = null;
89
90 protected final ComponentProvider componentProvider;
91 protected final D definition;
92 protected final Item relatedFieldItem;
93 protected AbstractOrderedLayout root;
94
95 protected AbstractCustomMultiField(D definition, FieldFactoryFactory fieldFactoryFactory, ComponentProvider componentProvider, Item relatedFieldItem, I18NAuthoringSupport i18nAuthoringSupport) {
96 this.definition = definition;
97 this.fieldFactoryFactory = fieldFactoryFactory;
98 this.componentProvider = componentProvider;
99 this.relatedFieldItem = relatedFieldItem;
100 }
101
102
103
104
105 @Deprecated
106 protected AbstractCustomMultiField(D definition, FieldFactoryFactory fieldFactoryFactory, I18nContentSupport i18nContentSupport, ComponentProvider componentProvider, Item relatedFieldItem) {
107 this(definition, fieldFactoryFactory, componentProvider, relatedFieldItem, componentProvider.getComponent(I18NAuthoringSupport.class));
108 }
109
110
111
112
113
114
115
116
117 protected abstract void initFields(T fieldValues);
118
119
120
121
122
123 @Override
124 public void setLocale(Locale locale) {
125 if (root != null) {
126 initFields();
127 }
128 super.setLocale(locale);
129 }
130
131 @SuppressWarnings("unchecked")
132 protected void initFields() {
133 T fieldValues = (T) getPropertyDataSource().getValue();
134 initFields(fieldValues);
135
136 if (relatedFieldItem instanceof ItemAdapter && ((ItemAdapter) relatedFieldItem).isNew() && !definition.isReadOnly()) {
137 getPropertyDataSource().setValue(getValue());
138 }
139 }
140
141
142
143
144 protected int findPropertyId(Item item, Property<?> property) {
145 Iterator<?> it = item.getItemPropertyIds().iterator();
146 while (it.hasNext()) {
147 Object pos = it.next();
148 if (pos.getClass().isAssignableFrom(Integer.class) && property == item.getItemProperty(pos)) {
149 return (Integer) pos;
150 } else {
151 log.debug("Property id {} is not an integer and as such property can't be located", pos);
152 }
153 }
154 return -1;
155 }
156
157
158
159
160 protected Field<?> createLocalField(FieldDefinition fieldDefinition, Property<?> property, boolean setCaptionToNull) {
161
162
163 FieldFactory fieldfactory = fieldFactoryFactory.createFieldFactory(fieldDefinition, holdsItem(property) ? property.getValue() : new NullItem());
164 fieldfactory.setComponentProvider(componentProvider);
165
166 if (fieldDefinition instanceof ConfiguredFieldDefinition) {
167 ((ConfiguredFieldDefinition) fieldDefinition).setI18nBasename(definition.getI18nBasename());
168 }
169 Field<?> field = fieldfactory.createField();
170
171
172
173 if (!holdsItem(property)) {
174 if (property != null && property.getValue() != null) {
175 field.setPropertyDataSource(property);
176 }
177 field.addValueChangeListener(selectionListener);
178 }
179
180
181 if (setCaptionToNull) {
182 field.setCaption(null);
183 } else if (StringUtils.isBlank(field.getCaption()) && StringUtils.isNotBlank(fieldDefinition.getLabel())) {
184 field.setCaption(fieldDefinition.getLabel());
185 }
186
187 field.setWidth(100, Unit.PERCENTAGE);
188
189
190 if (field instanceof AbstractCustomMultiField) {
191 ((AbstractCustomMultiField) field).setLocale(getLocale());
192 }
193
194
195 field.setReadOnly(fieldDefinition.isReadOnly());
196
197 return field;
198 }
199
200 boolean holdsItem(Property<?> property) {
201 return property != null && property.getValue() instanceof Item && !(property.getValue() instanceof PropertysetItem);
202 }
203
204
205
206
207 protected Property.ValueChangeListener selectionListener = new ValueChangeListener() {
208 @SuppressWarnings("unchecked")
209 @Override
210 public void valueChange(com.vaadin.v7.data.Property.ValueChangeEvent event) {
211 fireValueChange(false);
212
213
214 getPropertyDataSource().setValue(getValue());
215 }
216 };
217
218
219
220
221
222
223
224 @SuppressWarnings("unchecked")
225 protected List<AbstractField<T>> getFields(HasComponents root, boolean onlyValid) {
226 Iterator<Component> it = root.iterator();
227 List<AbstractField<T>> fields = new ArrayList<>();
228 while (it.hasNext()) {
229 Component c = it.next();
230 if (c instanceof AbstractField) {
231 if (!onlyValid || (((AbstractField<T>) c).isValid())) {
232 fields.add((AbstractField<T>) c);
233 }
234 } else if (c instanceof HasComponents) {
235 fields.addAll(getFields((HasComponents) c, onlyValid));
236 }
237 }
238 return fields;
239 }
240
241
242
243
244 @Override
245 public boolean isValid() {
246
247 if (!super.isValid()) {
248
249 markAsDirty();
250 return false;
251 }
252
253 List<AbstractField<T>> fields = getFields(this, false);
254 for (AbstractField<T> field : fields) {
255 if (!field.isVisible()) {
256 continue;
257 }
258 if (!field.isValid()) {
259
260 markAsDirty();
261 return false;
262 }
263 }
264 return true;
265 }
266
267
268
269
270 @Override
271 public ErrorMessage getErrorMessage() {
272 List<ErrorMessage> errors = new ArrayList<>();
273 ErrorMessage errorMessage = super.getErrorMessage();
274 if (errorMessage != null) {
275 errors.add(errorMessage);
276 }
277 List<AbstractField<T>> fields = getFields(this, false);
278
279 for (AbstractField<T> field : fields) {
280
281 if (!field.isVisible()) {
282 continue;
283 }
284 errorMessage = field.getErrorMessage();
285 if (errorMessage != null) {
286 String errorAsString = getFormattedHtmlAggregatedErrorMessage(errorMessage, field.getCaption());
287 errors.add(new UserError(errorAsString, ContentMode.HTML, errorMessage.getErrorLevel()));
288 }
289 }
290 return errors.isEmpty() ? null : new CompositeErrorMessage(errors);
291 }
292
293
294
295
296 @Override
297 public boolean isEmpty() {
298 List<AbstractField<T>> fields = getFields(this, false);
299 if (fields.isEmpty()) {
300 return true;
301 }
302 for (AbstractField<T> field : fields) {
303 if (!field.isVisible()) {
304 continue;
305 }
306 if (field.isEmpty()) {
307 return true;
308 }
309 }
310 return false;
311 }
312
313 private String getFormattedHtmlAggregatedErrorMessage(final ErrorMessage message, final String caption) {
314 return (StringUtils.isNotBlank(caption) ? caption + ": " : "") + StringUtils.join(ErrorMessageUtil.getCausesMessages(message), ", ");
315 }
316 }