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