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.editor;
35
36 import static java.util.Collections.singletonList;
37 import static java.util.stream.Collectors.toMap;
38
39 import info.magnolia.ui.datasource.PropertySetFactory;
40 import info.magnolia.ui.datasource.optionlist.Option;
41 import info.magnolia.ui.datasource.optionlist.OptionListDefinition;
42 import info.magnolia.ui.field.AbstractSelectFieldDefinition;
43 import info.magnolia.ui.field.FieldBinder;
44 import info.magnolia.ui.field.WithPropertyNameDecorator;
45 import info.magnolia.ui.framework.ioc.UiComponentProvider;
46 import info.magnolia.ui.framework.layout.PlainFormLayoutDefinition;
47
48 import java.util.Collections;
49 import java.util.List;
50 import java.util.Locale;
51 import java.util.Map;
52 import java.util.Optional;
53
54 import javax.inject.Inject;
55
56 import com.vaadin.data.Binder;
57 import com.vaadin.data.BinderValidationStatus;
58 import com.vaadin.data.HasValue;
59 import com.vaadin.ui.Component;
60 import com.vaadin.ui.Layout;
61 import com.vaadin.ui.VerticalLayout;
62
63
64
65
66
67
68
69
70 public class SwitchableFormView<T> implements EditorView<T> {
71
72 private final SwitchableFormDefinition<T> definition;
73
74 private final PropertySetFactory<T> propertySetFactory;
75
76 private final Binder<T> binder;
77
78 private final Map<String, EditorView<T>> representations;
79
80 private final Locale locale;
81
82 private VerticalLayout layout = new VerticalLayout();
83
84 @Inject
85 public SwitchableFormView(SwitchableFormDefinition<T> definition, PropertySetFactory<T> propertySetFactory, LocaleContext localeContext) {
86 this.definition = definition;
87 this.propertySetFactory = propertySetFactory;
88 this.representations = definition.getForms().stream()
89 .collect(toMap(
90 EditorDefinition::getName,
91 childEditor -> this.create(new CompositeFormDefinition<>(childEditor, definition.getPropertyNameDecorator()))));
92 this.locale = localeContext.getDefault();
93 this.binder = bindOptionField();
94
95 layout();
96 }
97
98 @Override
99 public void populate(T item) {
100 this.binder.readBean(item);
101 this.representations.values().forEach(representation -> representation.populate(item));
102
103 layout();
104 }
105
106 @Override
107 public List<BinderValidationStatus<?>> validate() {
108 Option currentRepresentation = getOptionField().getValue();
109 return this.subForm(currentRepresentation)
110 .map(EditorView::validate)
111 .orElseGet(Collections::emptyList);
112 }
113
114 @Override
115 public void write(T item) {
116 this.binder.writeBeanIfValid(item);
117 this.representations.values().forEach(representation -> representation.write(item));
118 }
119
120 private void layout() {
121 layout.removeAllComponents();
122 layout.setMargin(false);
123 HasValue<Option> optionField = getOptionField();
124 layout.addComponent((Component) optionField);
125
126 optionField.getOptionalValue()
127 .flatMap(this::subForm)
128 .ifPresent(view -> layout.addComponent(view.asVaadinComponent()));
129 }
130
131 private Binder<T> bindOptionField() {
132 AbstractSelectFieldDefinition<Option, OptionListDefinition> fieldDefinition = definition.getField();
133 UiComponentProvider componentProvider = getComponentProvider().inChildContext(fieldDefinition);
134 Binder<T> binder = Binder.withPropertySet(propertySetFactory.fromFieldDefinitions(singletonList(fieldDefinition), this.locale));
135
136 FieldBinder<Option> fieldBinder = componentProvider.newInstance(fieldDefinition.getFieldBinderClass());
137 HasValue<Option> optionsField = componentProvider.newInstance(fieldDefinition.getFactoryClass()).createField();
138 fieldBinder
139 .configureBinding(fieldDefinition, binder.forField(optionsField))
140 .bind(fieldDefinition.getName());
141
142 optionsField.addValueChangeListener(e -> {
143 subForm(e.getOldValue()).ifPresent(oldView -> layout.removeComponent(oldView.asVaadinComponent()));
144 subForm(e.getValue()).ifPresent(newView -> layout.addComponent(newView.asVaadinComponent()));
145 });
146
147 return binder;
148 }
149
150 private HasValue<Option> getOptionField() {
151 return binder.getBinding(definition.getField().getName())
152 .map(binding -> (HasValue<Option>) binding.getField())
153 .orElse(null);
154 }
155
156 private Optional<EditorView<T>> subForm(Option selectedOption) {
157 return Optional.ofNullable(selectedOption).map(option -> representations.get(option.getValue()));
158 }
159
160 private class CompositeFormDefinition<T> extends ConfiguredFormDefinition<T> implements WithPropertyNameDecorator {
161
162 private final Class<PropertyNameDecorator> propertyNameDecorator;
163
164 public CompositeFormDefinition(FormDefinition<T> definition, Class<PropertyNameDecorator> propertyNameDecorator) {
165 this.setLayout(new PlainFormLayoutDefinition());
166 this.setName(definition.getName());
167 this.setProperties(definition.getProperties());
168 this.propertyNameDecorator = propertyNameDecorator;
169 }
170
171 @Override
172 public Class getPropertyNameDecorator() {
173 return propertyNameDecorator;
174 }
175 }
176
177 @Override
178 public Component asVaadinComponent() {
179 return layout;
180 }
181 }