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.DefaultProperty;
56 import info.magnolia.ui.vaadin.integration.jcr.DefaultPropertyUtil;
57 import info.magnolia.util.EnumCaseInsensitive;
58
59 import java.util.Locale;
60
61 import javax.inject.Inject;
62
63 import org.apache.commons.lang3.StringUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 import com.vaadin.data.Item;
68 import com.vaadin.data.Property;
69 import com.vaadin.data.util.BeanItem;
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 && !definition.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 protected Object getConfiguredDefaultValue() {
256 return definition.getDefaultValue();
257 }
258
259 @Override
260 public D getFieldDefinition() {
261 return this.definition;
262 }
263
264
265
266
267 protected abstract Field<T> createFieldComponent();
268
269 @Override
270 public View getView() {
271 final CssLayout fieldView = new CssLayout();
272 fieldView.setStyleName("field-view");
273
274 Label label = new Label();
275 label.setSizeUndefined();
276 label.setCaption(getFieldDefinition().getLabel());
277
278 if (getFieldDefinition().getClass().isAssignableFrom(TextFieldDefinition.class)) {
279 final TextFieldDefinition textFieldDefinition = (TextFieldDefinition) getFieldDefinition();
280 if (textFieldDefinition.getRows() > 0) {
281 label.addStyleName("textarea");
282 }
283 }
284 if (definition.getConverterClass() != null) {
285 Converter converter = initializeConverter(definition.getConverterClass());
286 label.setConverter(converter);
287 }
288
289 Property<?> property = initializeProperty();
290
291 label.setPropertyDataSource(property);
292
293 fieldView.addComponent(label);
294
295 return new View() {
296 @Override
297 public Component asVaadinComponent() {
298 return fieldView;
299 }
300 };
301 }
302
303
304
305
306
307 @SuppressWarnings("unchecked")
308 protected Property<T> initializeProperty() {
309
310 boolean useTransformers = !(item instanceof BeanItem);
311
312 if (useTransformers) {
313 Class<? extends Transformer<?>> transformerClass = definition.getTransformerClass();
314 if (transformerClass == null) {
315
316
317
318
319 transformerClass = (Class<? extends Transformer<?>>) (Object) BasicTransformer.class;
320 }
321 Transformer<?> transformer = initializeTransformer(transformerClass);
322 transformer.setLocale(locale);
323 return new TransformedProperty(transformer);
324
325 } else {
326
327 Property property = item.getItemProperty(definition.getName());
328 if (property == null) {
329 log.warn(String.format("BeanItem doesn't have any property for id %s, returning default property", definition.getName()));
330 Class<?> propertyType = DefaultPropertyUtil.getFieldTypeClass(definition.getType());
331 property = new DefaultProperty(propertyType, null);
332 item.addItemProperty(definition.getName(), property);
333 }
334 return property;
335 }
336 }
337
338
339
340
341
342 protected Transformer<?> initializeTransformer(Class<? extends Transformer<?>> transformerClass) {
343 return this.componentProvider.newInstance(transformerClass, item, definition, getFieldType(), i18NAuthoringSupport);
344 }
345
346
347
348
349
350 protected Converter<?, ?> initializeConverter(Class<? extends Converter<?, ?>> converterClass) {
351 if (converter == null) {
352 converter = componentProvider.newInstance(converterClass);
353 }
354 return converter;
355 }
356
357
358
359
360
361
362
363
364 protected Class<?> getFieldType() {
365 Class<?> type = getDefinitionType();
366 if (type == null && definition.getConverterClass() != null) {
367 Converter converter = initializeConverter(definition.getConverterClass());
368 type = converter.getModelType();
369 }
370 if (type == null) {
371 type = getDefaultFieldType();
372 }
373 return type;
374 }
375
376
377
378
379 protected Class<?> getDefinitionType() {
380 if (StringUtils.isNotBlank(definition.getType())) {
381 if (DefaultPropertyUtil.isKnownJcrTypeName(definition.getType())) {
382 return DefaultPropertyUtil.getFieldTypeClass(definition.getType());
383 } else try {
384 return Classes.getClassFactory().forName(definition.getType());
385 } catch (ClassNotFoundException e) {
386 log.error("Unknown configured type {}", definition.getType());
387 }
388 }
389 return null;
390 }
391
392
393
394
395 protected Class<?> getDefaultFieldType() {
396 return UndefinedPropertyType.class;
397 }
398
399 @Override
400 protected String getI18nBasename() {
401 return definition.getI18nBasename();
402 }
403
404
405
406
407
408
409
410 private void setConstraints() {
411
412 for (FieldValidatorDefinition validatorDefinition : definition.getValidators()) {
413 FieldValidatorFactory validatorFactory = this.fieldValidatorFactoryFactory.createFieldValidatorFactory(validatorDefinition, item);
414 if (validatorFactory != null) {
415 field.addValidator(validatorFactory.createValidator());
416 } else {
417 log.warn("Not able to create Validation for the following definition {}", definition.toString());
418 }
419 }
420
421 if (definition.isRequired()) {
422 field.setInvalidCommitted(true);
423 field.setRequired(true);
424 field.setRequiredError(definition.getRequiredErrorMessage());
425 }
426
427
428 if (field.getPropertyDataSource() != null) {
429 field.getPropertyDataSource().setReadOnly(definition.isReadOnly());
430 }
431 }
432
433 @Override
434 public void setComponentProvider(ComponentProvider componentProvider) {
435 this.componentProvider = componentProvider;
436 if (uiContext == null) {
437 uiContext = componentProvider.getComponent(UiContext.class);
438 }
439 }
440
441 protected ComponentProvider getComponentProvider() {
442 return componentProvider;
443 }
444
445 public void setLocale(Locale locale) {
446 this.locale = locale;
447 }
448
449 public Locale getLocale() {
450 return locale;
451 }
452 }