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