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.jcr.util.NodeUtil;
37 import info.magnolia.jcr.util.SessionUtil;
38 import info.magnolia.objectfactory.Components;
39 import info.magnolia.ui.api.context.UiContext;
40 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
41 import info.magnolia.ui.form.field.definition.SelectFieldDefinition;
42 import info.magnolia.ui.form.field.definition.SelectFieldOptionDefinition;
43 import info.magnolia.ui.form.field.transformer.UndefinedPropertyType;
44
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.Comparator;
48 import java.util.HashSet;
49 import java.util.Iterator;
50 import java.util.List;
51
52 import javax.inject.Inject;
53 import javax.jcr.Node;
54 import javax.jcr.RepositoryException;
55
56 import org.apache.commons.collections4.ComparatorUtils;
57 import org.apache.commons.lang3.StringUtils;
58 import org.apache.jackrabbit.commons.predicate.Predicate;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.vaadin.server.Resource;
63 import com.vaadin.server.ThemeResource;
64 import com.vaadin.v7.data.Item;
65 import com.vaadin.v7.data.Property;
66 import com.vaadin.v7.data.util.IndexedContainer;
67 import com.vaadin.v7.ui.AbstractSelect;
68 import com.vaadin.v7.ui.AbstractSelect.ItemCaptionMode;
69 import com.vaadin.v7.ui.ComboBox;
70
71
72
73
74
75
76 public class SelectFieldFactory<D extends SelectFieldDefinition> extends AbstractFieldFactory<D, Object> {
77
78 private static final Logger log = LoggerFactory.getLogger(SelectFieldFactory.class);
79
80 private List<String> initialSelectedKeys = new ArrayList<>();
81 private String optionValueName;
82 private String optionLabelName;
83 private final String optionIconName = SelectFieldDefinition.OPTION_ICONSRC_PROPERTY_NAME;
84 private boolean hasOptionIcon = false;
85 private boolean sortOptions = true;
86
87 protected AbstractSelect select;
88
89 @Inject
90 public SelectFieldFactory(D definition, Item relatedFieldItem, UiContext uiContext, I18NAuthoringSupport i18nAuthoringSupport) {
91 super(definition, relatedFieldItem, uiContext, i18nAuthoringSupport);
92 }
93
94
95
96
97 @Deprecated
98 public SelectFieldFactory(D definition, Item relatedFieldItem) {
99 this(definition, relatedFieldItem, null, Components.getComponent(I18NAuthoringSupport.class));
100 }
101
102 @Override
103 protected AbstractSelect createFieldComponent() {
104
105 optionValueName = definition.getValueProperty();
106 optionLabelName = definition.getLabelProperty();
107 sortOptions = definition.isSortOptions();
108
109 select = createSelectionField();
110 select.setContainerDataSource(buildOptions());
111 select.setNullSelectionAllowed(false);
112 select.setMultiSelect(false);
113 select.setNewItemsAllowed(false);
114 if (select instanceof ComboBox) {
115 ((ComboBox) select).setFilteringMode(definition.getFilteringMode());
116 ((ComboBox) select).setTextInputAllowed(definition.isTextInputAllowed());
117 ((ComboBox) select).setPageLength(definition.getPageLength());
118 }
119 select.setItemCaptionMode(ItemCaptionMode.PROPERTY);
120 select.setItemCaptionPropertyId(optionLabelName);
121
122 return select;
123 }
124
125
126
127
128 protected AbstractSelect createSelectionField() {
129 return new ComboBox();
130 }
131
132
133
134
135
136
137
138
139 @SuppressWarnings("unchecked")
140 private IndexedContainer buildOptions() {
141 IndexedContainer optionContainer = new IndexedContainer();
142
143 List<SelectFieldOptionDefinition> options = getOptions();
144 sortOptions(options);
145
146 if (!options.isEmpty()) {
147 Class<?> resolvedFieldType = getFieldType();
148
149 Class<?> fieldType = resolvedFieldType != UndefinedPropertyType.class ? resolvedFieldType : String.class;
150 optionContainer.addContainerProperty(optionValueName, fieldType, null);
151 optionContainer.addContainerProperty(optionLabelName, String.class, null);
152 if (hasOptionIcon) {
153 optionContainer.addContainerProperty(optionIconName, Resource.class, null);
154 }
155 for (SelectFieldOptionDefinition option : options) {
156 Object value = createTypedValue(option.getValue(), fieldType);
157 Item item = optionContainer.addItem(value);
158 item.getItemProperty(optionValueName).setValue(value);
159 item.getItemProperty(optionLabelName).setValue(option.getLabel());
160 if (StringUtils.isNotBlank(option.getIconSrc())) {
161 item.getItemProperty(optionIconName).setValue(getIconResource(option));
162 }
163 }
164 }
165 return optionContainer;
166 }
167
168 private void sortOptions(List<SelectFieldOptionDefinition> options) {
169 if (sortOptions) {
170 if (definition.getComparatorClass() != null) {
171 Comparator<SelectFieldOptionDefinition> comparator = initializeComparator(definition.getComparatorClass());
172 Collections.sort(options, comparator);
173 } else {
174 Collections.sort(options, new DefaultOptionComparator());
175 }
176 }
177 }
178
179 protected Comparator<SelectFieldOptionDefinition> initializeComparator(Class<? extends Comparator<SelectFieldOptionDefinition>> comparatorClass) {
180 return getComponentProvider().newInstance(comparatorClass, item, definition, getFieldType());
181 }
182
183
184
185
186
187
188
189
190
191
192 public List<SelectFieldOptionDefinition> getOptions() {
193
194
195 return getSelectFieldOptionDefinition();
196 }
197
198
199
200
201 @Deprecated
202 public List<SelectFieldOptionDefinition> getSelectFieldOptionDefinition() {
203 List<SelectFieldOptionDefinition> res = new ArrayList<>();
204
205 if (definition.getOptions() != null && !definition.getOptions().isEmpty()) {
206 for (SelectFieldOptionDefinition option : definition.getOptions()) {
207 option.setValue(getValue(option));
208 if (option.isSelected()) {
209 initialSelectedKeys.add(getValue(option));
210 }
211 if (!hasOptionIcon && StringUtils.isNotBlank(option.getIconSrc())) {
212 hasOptionIcon = true;
213 }
214 res.add(option);
215 }
216 } else if (StringUtils.isNotBlank(definition.getPath())) {
217
218 buildRemoteOptions(res);
219 }
220
221 return res;
222 }
223
224
225
226
227
228
229 public Resource getIconResource(SelectFieldOptionDefinition option) {
230 return new ThemeResource(option.getIconSrc());
231 }
232
233
234
235
236
237
238
239 private String getValue(SelectFieldOptionDefinition option) {
240 if (StringUtils.isBlank(option.getValue())) {
241 if (StringUtils.isNotBlank(option.getName())) {
242 return option.getName();
243 } else {
244 return option.getLabel();
245 }
246 } else {
247 return option.getValue();
248 }
249 }
250
251
252
253
254 @Override
255 protected Object createDefaultValue(Property property) {
256 Object defaultValue = super.createDefaultValue(property);
257 if (defaultValue == null) {
258 boolean shouldPreselectFirstValue = !select.isNullSelectionAllowed() && !select.isMultiSelect() && property.getValue() == null;
259 if (shouldPreselectFirstValue) {
260
261 if (select.getItemIds() != null && !select.getItemIds().isEmpty()) {
262 defaultValue = select.getItemIds().iterator().next();
263 }
264 }
265 }
266 return defaultValue;
267 }
268
269
270
271
272
273 protected Object getConfiguredDefaultValue() {
274 if (initialSelectedKeys.isEmpty()) {
275 return null;
276 }
277 if (select.isMultiSelect()) {
278 return new HashSet<>(initialSelectedKeys);
279 } else {
280 return initialSelectedKeys.get(0);
281 }
282 }
283
284
285
286
287
288
289
290 private void buildRemoteOptions(List<SelectFieldOptionDefinition> res) {
291 Node parent = SessionUtil.getNode(definition.getRepository(), definition.getPath());
292 if (parent != null) {
293 try {
294
295 Iterable<Node> iterable = NodeUtil.getNodes(parent, createRemoteOptionFilterPredicate());
296 Iterator<Node> iterator = iterable.iterator();
297
298 while (iterator.hasNext()) {
299 SelectFieldOptionDefinition option = new SelectFieldOptionDefinition();
300 Node child = iterator.next();
301
302 String label = getRemoteOptionsName(child, optionLabelName);
303 String value = getRemoteOptionsValue(child, optionValueName);
304 option.setLabel(label);
305 option.setValue(value);
306
307 if (child.hasProperty(SelectFieldDefinition.OPTION_SELECTED_PROPERTY_NAME) && Boolean.parseBoolean(child.getProperty(SelectFieldDefinition.OPTION_SELECTED_PROPERTY_NAME).getString())) {
308 option.setSelected(true);
309 initialSelectedKeys.add(option.getValue());
310 }
311 if (child.hasProperty(SelectFieldDefinition.OPTION_NAME_PROPERTY_NAME)) {
312 option.setName(child.getProperty(SelectFieldDefinition.OPTION_NAME_PROPERTY_NAME).getString());
313 }
314 if (child.hasProperty(SelectFieldDefinition.OPTION_ICONSRC_PROPERTY_NAME)) {
315 option.setIconSrc(child.getProperty(SelectFieldDefinition.OPTION_ICONSRC_PROPERTY_NAME).getString());
316 hasOptionIcon = true;
317 }
318 res.add(option);
319 }
320 } catch (Exception e) {
321 log.warn("Not able to build options based on option node " + parent.toString(), e);
322 }
323 }
324 }
325
326
327
328
329 protected Predicate createRemoteOptionFilterPredicate() {
330 return NodeUtil.MAGNOLIA_FILTER;
331 }
332
333
334
335
336
337
338 protected String getRemoteOptionsName(Node option, String propertyName) throws RepositoryException {
339 if (option.hasProperty(propertyName)) {
340 return option.getProperty(propertyName).getString();
341 } else {
342 return option.getName();
343 }
344 }
345
346
347
348
349
350
351 protected String getRemoteOptionsValue(Node option, String propertyName) throws RepositoryException {
352 return getRemoteOptionsName(option, propertyName);
353 }
354
355
356
357
358 public static class DefaultOptionComparator implements Comparator<SelectFieldOptionDefinition> {
359
360 @Override
361 public int compare(SelectFieldOptionDefinition def, SelectFieldOptionDefinition otherDef) {
362
363 return ComparatorUtils.nullLowComparator(String.CASE_INSENSITIVE_ORDER).compare(def.getLabel(), otherDef.getLabel());
364 }
365 }
366 }