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