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.factory;
35
36 import info.magnolia.cms.i18n.I18nContentSupport;
37 import info.magnolia.objectfactory.Classes;
38 import info.magnolia.objectfactory.ComponentProvider;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.ui.api.app.SubAppContext;
41 import info.magnolia.ui.api.context.UiContext;
42 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
43 import info.magnolia.ui.api.view.View;
44 import info.magnolia.ui.form.AbstractFormItem;
45 import info.magnolia.ui.form.field.converter.Vaadin7FieldValueConverterAdapter;
46 import info.magnolia.ui.form.field.definition.FieldDefinition;
47 import info.magnolia.ui.form.field.definition.TextFieldDefinition;
48 import info.magnolia.ui.form.field.transformer.TransformedProperty;
49 import info.magnolia.ui.form.field.transformer.Transformer;
50 import info.magnolia.ui.form.field.transformer.UndefinedPropertyType;
51 import info.magnolia.ui.form.field.transformer.basic.BasicTransformer;
52 import info.magnolia.ui.form.validator.definition.FieldValidatorDefinition;
53 import info.magnolia.ui.form.validator.factory.FieldValidatorFactory;
54 import info.magnolia.ui.form.validator.registry.FieldValidatorFactoryFactory;
55 import info.magnolia.ui.vaadin.integration.ItemAdapter;
56 import info.magnolia.ui.vaadin.integration.jcr.DefaultPropertyUtil;
57 import info.magnolia.util.EnumCaseInsensitive;
58
59 import java.util.Collections;
60 import java.util.Locale;
61 import java.util.Objects;
62 import java.util.Optional;
63
64 import javax.inject.Inject;
65
66 import org.apache.commons.lang3.StringUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.vaadin.server.Sizeable.Unit;
71 import com.vaadin.ui.Component;
72 import com.vaadin.ui.CssLayout;
73 import com.vaadin.v7.data.Item;
74 import com.vaadin.v7.data.Property;
75 import com.vaadin.v7.data.util.BeanItem;
76 import com.vaadin.v7.data.util.ObjectProperty;
77 import com.vaadin.v7.data.util.converter.Converter;
78 import com.vaadin.v7.data.util.converter.ConverterUtil;
79 import com.vaadin.v7.ui.AbstractField;
80 import com.vaadin.v7.ui.Field;
81 import com.vaadin.v7.ui.Label;
82
83
84
85
86
87
88
89
90
91
92
93
94 @Deprecated
95 public abstract class AbstractFieldFactory<D extends FieldDefinition, T> extends AbstractFormItem implements FieldFactory {
96
97 private static final Logger log = LoggerFactory.getLogger(AbstractFieldFactory.class);
98
99 protected Field<T> field;
100 protected D definition;
101 protected Item item;
102
103 private FieldValidatorFactoryFactory fieldValidatorFactoryFactory;
104 private I18NAuthoringSupport i18NAuthoringSupport;
105 private ComponentProvider componentProvider;
106 private UiContext uiContext;
107 private Locale locale;
108 private Converter<?, ?> converter;
109
110 @Inject
111 public AbstractFieldFactory(D definition, Item relatedFieldItem, UiContext uiContext, I18NAuthoringSupport i18NAuthoringSupport) {
112 this.definition = definition;
113 this.item = relatedFieldItem;
114 this.uiContext = uiContext;
115 this.i18NAuthoringSupport = i18NAuthoringSupport;
116 }
117
118
119
120
121 @Deprecated
122 public AbstractFieldFactory(D definition, Item relatedFieldItem) {
123
124
125 this(definition, relatedFieldItem, null, Components.getComponent(I18NAuthoringSupport.class));
126 }
127
128 @Override
129 public void setFieldValidatorFactoryFactory(FieldValidatorFactoryFactory fieldValidatorFactoryFactory) {
130 this.fieldValidatorFactoryFactory = fieldValidatorFactoryFactory;
131 }
132
133
134
135
136
137 @Override
138 @Deprecated
139 public void setI18nContentSupport(I18nContentSupport i18nContentSupport) {
140 }
141
142 @Override
143 public Field<T> createField() {
144 if (locale == null) {
145 Locale locale = determineCurrentLocale();
146 setLocale(locale);
147 }
148
149 if (field == null) {
150
151 this.field = createFieldComponent();
152
153 if (field instanceof AbstractField) {
154 final AbstractField field = (AbstractField) this.field;
155 if (definition.getConverterClass() != null) {
156 Converter<?, ?> converter = initializeConverter(definition.getConverterClass());
157 if (!field.getType().isAssignableFrom(converter.getModelType())) {
158
159
160 field.setConverter(converter);
161 }
162 }
163 field.setLocale(locale);
164 }
165
166 Property<?> property = initializeProperty();
167
168 setPropertyDataSourceAndDefaultValue(property);
169
170 if (StringUtils.isNotBlank(definition.getStyleName())) {
171 this.field.addStyleName(definition.getStyleName());
172 }
173
174 field.setWidth(100, Unit.PERCENTAGE);
175
176 setFieldCaption();
177 setConstraints();
178
179 }
180 return this.field;
181 }
182
183
184 private void setFieldCaption() {
185
186 if (StringUtils.isNotBlank(getFieldDefinition().getLabel())) {
187 String caption = getFieldDefinition().getLabel();
188
189 if (locale != null && definition.isI18n()) {
190 caption = String.format("%s (%s)", caption, locale.toString());
191 }
192
193 this.field.setCaption(caption);
194 }
195 }
196
197
198
199
200
201
202
203
204 public void setPropertyDataSourceAndDefaultValue(Property property) {
205 this.field.setPropertyDataSource(property);
206
207 if ((item instanceof ItemAdapterItemAdapterinfo/magnolia/ui/vaadin/integration/ItemAdapter.html#ItemAdapter">ItemAdapter && ((ItemAdapterItemAdapterNew() && property.getValue() == null) || (!(item instanceof ItemAdapter) && property.getValue() == null)) {
208 setPropertyDataSourceDefaultValue(property);
209 }
210 }
211
212
213
214
215 protected void setPropertyDataSourceDefaultValue(Property property) {
216 Object defaultValue = createDefaultValue(property);
217 if (defaultValue != null && !property.isReadOnly()) {
218 if (property.getType().isAssignableFrom(defaultValue.getClass())) {
219 property.setValue(defaultValue);
220 } else {
221 log.warn("Default value {} cannot be assigned to property of type {}.", defaultValue, property.getType());
222 }
223 }
224 }
225
226 protected Object createDefaultValue(Property property) {
227 Object defaultValue = getConfiguredDefaultValue();
228 Class<?> propertyType = property != null ? property.getType() : getFieldType();
229 return createTypedValue(defaultValue, propertyType);
230 }
231
232
233
234
235
236 protected Object createTypedValue(Object defaultValue, Class<?> propertyType) {
237
238 if (defaultValue instanceof String && DefaultPropertyUtil.canConvertStringValue(propertyType)) {
239 return DefaultPropertyUtil.createTypedValue(propertyType, (String) defaultValue);
240
241 } else if (defaultValue != null && definition.getConverterClass() != null) {
242
243
244 Converter converter = initializeConverter(definition.getConverterClass());
245 Class<?> modelType = propertyType != null ? propertyType : converter.getModelType();
246 Locale locale = Locale.ENGLISH;
247 try {
248 defaultValue = ConverterUtil.convertToModel(defaultValue, modelType, converter, locale);
249 } catch (Converter.ConversionException e) {
250 log.error("Default value {} could not be converted to property type {}.", defaultValue, propertyType, e);
251 }
252
253 } else if (propertyType != null && propertyType.isEnum() && defaultValue instanceof String) {
254 Class<? extends Enum> enumType = (Class<? extends Enum>) propertyType;
255 EnumCaseInsensitive enumFinder = new EnumCaseInsensitive();
256 defaultValue = enumFinder.valueOf(enumType, (String) defaultValue);
257 }
258
259 return defaultValue;
260 }
261
262
263
264
265
266
267 protected Object getConfiguredDefaultValue() {
268 return definition.getDefaultValue();
269 }
270
271 @Override
272 public D getFieldDefinition() {
273 return this.definition;
274 }
275
276
277
278
279 protected abstract Field<T> createFieldComponent();
280
281 @Override
282 public View getView() {
283 final CssLayout fieldView = new CssLayout();
284 fieldView.setStyleName("field-view");
285
286 Label label = new Label();
287 label.setSizeUndefined();
288 label.setCaption(getFieldDefinition().getLabel());
289
290 if (getFieldDefinition().getClass().isAssignableFrom(TextFieldDefinition.class)) {
291 final TextFieldDefinitionnolia/ui/form/field/definition/TextFieldDefinition.html#TextFieldDefinition">TextFieldDefinition textFieldDefinition = (TextFieldDefinition) getFieldDefinition();
292 if (textFieldDefinition.getRows() > 0) {
293 label.addStyleName("textarea");
294 }
295 }
296 if (definition.getConverterClass() != null) {
297 Converter converter = initializeConverter(definition.getConverterClass());
298 label.setConverter(converter);
299 }
300
301 Property<?> property = initializeProperty();
302
303 label.setPropertyDataSource(property);
304
305 fieldView.addComponent(label);
306
307 return new View() {
308 @Override
309 public Component asVaadinComponent() {
310 return fieldView;
311 }
312 };
313 }
314
315
316
317
318
319 @SuppressWarnings("unchecked")
320 protected Property<T> initializeProperty() {
321
322 boolean useTransformers = !(item instanceof BeanItem);
323
324 if (useTransformers) {
325 Class<? extends Transformer<?>> transformerClass = definition.getTransformerClass();
326 if (transformerClass == null) {
327
328
329
330
331 transformerClass = (Class<? extends Transformer<?>>) (Object) BasicTransformer.class;
332 }
333 Transformer<?> transformer = initializeTransformer(transformerClass);
334 transformer.setLocale(locale);
335 return new TransformedProperty(transformer);
336
337 } else {
338
339 Property property = item.getItemProperty(definition.getName());
340 if (property == null) {
341 log.warn(String.format("BeanItem doesn't have any property for id %s, returning default property", definition.getName()));
342 Class<?> propertyType = DefaultPropertyUtil.getFieldTypeClass(definition.getType());
343 property = new ObjectProperty<>(null, propertyType);
344 item.addItemProperty(definition.getName(), property);
345 }
346 return property;
347 }
348 }
349
350
351
352
353
354 protected Transformer<?> initializeTransformer(Class<? extends Transformer<?>> transformerClass) {
355 return this.componentProvider.newInstance(transformerClass, item, definition, getFieldType(), i18NAuthoringSupport);
356 }
357
358
359
360
361
362 protected Converter<?, ?> initializeConverter(Class<?> converterClass) {
363 if (converter == null) {
364 converter = Vaadin7FieldValueConverterAdapter.wrap(componentProvider.newInstance(converterClass));
365 }
366 return converter;
367 }
368
369
370
371
372
373
374
375
376 protected Class<?> getFieldType() {
377 Class<?> type = getDefinitionType();
378 if (type == null && definition.getConverterClass() != null) {
379 Converter converter = initializeConverter(definition.getConverterClass());
380 type = converter.getModelType();
381 }
382 if (type == null) {
383 type = getDefaultFieldType();
384 }
385 return type;
386 }
387
388
389
390
391 protected Class<?> getDefinitionType() {
392 if (StringUtils.isNotBlank(definition.getType())) {
393 if (DefaultPropertyUtil.isKnownJcrTypeName(definition.getType())) {
394 return DefaultPropertyUtil.getFieldTypeClass(definition.getType());
395 } else try {
396 return Classes.getClassFactory().forName(definition.getType());
397 } catch (ClassNotFoundException e) {
398 log.warn("Unknown configured type {}", definition.getType());
399 }
400 }
401 return null;
402 }
403
404
405
406
407 protected Class<?> getDefaultFieldType() {
408 return UndefinedPropertyType.class;
409 }
410
411 @Override
412 protected String getI18nBasename() {
413 return definition.getI18nBasename();
414 }
415
416
417
418
419
420
421 protected boolean isMessageKey(String key) {
422 return !StringUtils.endsWith(key, ".") && StringUtils.contains(key, ".") && !StringUtils.contains(key, " ");
423 }
424
425
426
427
428
429
430
431 private void setConstraints() {
432
433 for (FieldValidatorDefinition validatorDefinition : definition.getValidators()) {
434 FieldValidatorFactory validatorFactory = this.fieldValidatorFactoryFactory.createFieldValidatorFactory(validatorDefinition, item);
435 if (validatorFactory != null) {
436 field.addValidator(validatorFactory.createValidator());
437 } else {
438 log.warn("Not able to create Validation for the following definition {}", definition.toString());
439 }
440 }
441
442 if (definition.isRequired()) {
443 field.setInvalidCommitted(true);
444 field.setRequired(true);
445 field.setRequiredError(definition.getRequiredErrorMessage());
446 }
447
448
449
450
451 if (definition.isReadOnly()) {
452 field.setReadOnly(true);
453 }
454 }
455
456 @Override
457 public void setComponentProvider(ComponentProvider componentProvider) {
458 this.componentProvider = componentProvider;
459 if (uiContext == null) {
460 uiContext = componentProvider.getComponent(UiContext.class);
461 }
462 }
463
464 protected ComponentProvider getComponentProvider() {
465 return componentProvider;
466 }
467
468 public void setLocale(Locale locale) {
469 this.locale = locale;
470 }
471
472 public Locale getLocale() {
473 return locale;
474 }
475
476
477
478
479 private Locale determineCurrentLocale() {
480 if (uiContext instanceof SubAppContext) {
481 Locale authoringLocale = ((SubAppContext) uiContext).getAuthoringLocale();
482 Optional<Locale> locale = Optional.ofNullable(i18NAuthoringSupport.getAvailableLocales(item))
483 .orElse(Collections.emptyList())
484 .stream()
485 .filter(Objects::nonNull)
486 .filter(l -> l.equals(authoringLocale))
487 .findFirst();
488 if (locale.isPresent()) {
489 return authoringLocale;
490 } else {
491 return i18NAuthoringSupport.getDefaultLocale(item);
492 }
493 }
494 return i18NAuthoringSupport.getDefaultLocale(item);
495 }
496 }