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.stream.Collectors.*;
37
38 import info.magnolia.ui.field.NoopNameDecorator;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
41 import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;
42 import info.magnolia.ui.field.FieldDefinition;
43 import info.magnolia.ui.field.WithPropertyNameDecorator.PropertyNameDecorator;
44
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.List;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.Optional;
51 import java.util.stream.Stream;
52
53 import javax.jcr.Item;
54 import javax.jcr.Node;
55
56 import com.machinezoo.noexception.Exceptions;
57 import com.vaadin.data.PropertyDefinition;
58 import com.vaadin.data.PropertySet;
59 import com.vaadin.data.ValueProvider;
60 import com.vaadin.server.Setter;
61
62 import lombok.Builder;
63 import lombok.Data;
64
65
66
67
68
69
70
71
72
73
74 public class JcrItemPropertySet implements PropertySet<Item> {
75
76
77
78
79
80
81
82 public static JcrItemPropertySet withProperties(Map<String, Class> properties) {
83 List<JcrItemPropertySet.JcrPropertyDescriptor> propertyDescriptors = properties.entrySet().stream()
84 .map(entry -> JcrItemPropertySet.JcrPropertyDescriptor.builder()
85 .type(entry.getValue())
86 .name(entry.getKey())
87 .build())
88 .collect(toList());
89
90 return new JcrItemPropertySet(propertyDescriptors, Components.getComponent(I18NAuthoringSupport.class));
91 }
92
93
94
95
96
97
98
99 public static <T> PropertySet<Item> fromColumns(List<ColumnDefinition<T>> columnDefinitions) {
100 return withProperties(columnDefinitions.stream().collect(toMap(ColumnDefinition::getName, ColumnDefinition::getType)));
101 }
102
103
104
105
106
107
108
109 public static PropertySet<Item> fromFieldDefinitions(Collection<FieldDefinition> fieldDefinitions, Locale locale) {
110
111 return new JcrItemPropertySet(fieldDefinitions.stream()
112 .map(fieldDefinition -> JcrPropertyDescriptor
113 .builder()
114 .name(fieldDefinition.getName())
115 .isReadonly(fieldDefinition.isReadOnly())
116 .type(fieldDefinition.getType())
117 .build())
118 .collect(toList()), locale, Components.getComponent(I18NAuthoringSupport.class), null);
119 }
120
121 private final List<PropertyDefinition<Item, ?>> knownProperties = new ArrayList<>();
122 private final Locale locale;
123 private final I18NAuthoringSupport<Item> genericLocalisationSupport;
124 private final PropertyNameDecorator propertyNameDecorator;
125
126 public JcrItemPropertySet(List<JcrPropertyDescriptor> knownProperties, I18NAuthoringSupport<Item> genericLocalisationSupport) {
127 this(knownProperties, null, genericLocalisationSupport, new NoopNameDecorator());
128 }
129
130 public JcrItemPropertySet(List<JcrPropertyDescriptor> knownProperties, Locale locale, I18NAuthoringSupport<Item> genericLocalisationSupport) {
131 this(knownProperties, locale, genericLocalisationSupport, new NoopNameDecorator());
132 }
133
134 public JcrItemPropertySet(List<JcrPropertyDescriptor> knownProperties, Locale locale, I18NAuthoringSupport<Item> genericLocalisationSupport, PropertyNameDecorator propertyNameDecorator) {
135 this.locale = locale;
136 this.genericLocalisationSupport = genericLocalisationSupport;
137 this.propertyNameDecorator = propertyNameDecorator;
138
139 knownProperties.forEach(property -> this.knownProperties.add(new JcrItemPropertyDefinition<>(this, property)));
140 }
141
142 @Override
143 public Stream<PropertyDefinition<Item, ?>> getProperties() {
144 return knownProperties.stream();
145 }
146
147 @Override
148 public Optional<PropertyDefinition<Item, ?>> getProperty(String name) {
149 return Optional.of(knownProperties.stream()
150 .filter(def -> def.getName().equals(name))
151 .findFirst()
152 .orElse(getDecoratedProperty(name)));
153 }
154
155 private PropertyDefinition<Item, ?> getDecoratedProperty(String name) {
156 return Optional.of(knownProperties.stream()
157 .filter(def -> def.getName().equals(propertyNameDecorator.apply(name)))
158 .findFirst()
159 .orElse(new JcrItemPropertyDefinition<>(this, JcrItemPropertySet.JcrPropertyDescriptor.builder()
160 .type(Object.class)
161 .name(name)
162 .build()))).get();
163 }
164
165
166
167
168
169
170 @Data
171 public static class JcrPropertyDescriptor<T> {
172 private Class<T> type;
173 private String name;
174 private boolean isReadonly;
175 private boolean isI18n;
176 private Locale locale;
177 private T defaultValue;
178 private PropertyNameDecorator propertyNameDecorator = new NoopNameDecorator();
179
180 public JcrPropertyDescriptor() {
181 }
182
183 @Builder
184 JcrPropertyDescriptor(Class<T> type, String name, boolean isReadonly, boolean isI18n, Locale locale, T defaultValue, PropertyNameDecorator propertyNameDecorator) {
185 this.type = type;
186 this.name = name;
187 this.isReadonly = isReadonly;
188 this.isI18n = isI18n;
189 this.locale = locale;
190 this.defaultValue = defaultValue;
191 if (propertyNameDecorator != null) {
192 this.propertyNameDecorator = propertyNameDecorator;
193 }
194 }
195
196 public String getName() {
197 return this.propertyNameDecorator.apply(this.name);
198 }
199 }
200
201
202
203
204
205
206
207
208 class JcrPropertyDescriptorWrapper<T> extends JcrPropertyDescriptor<T> {
209
210 private JcrPropertyDescriptor<T> delegate;
211
212 public JcrPropertyDescriptorWrapper(JcrPropertyDescriptor<T> delegate) {
213 this.delegate = delegate;
214 }
215
216 @Override
217 public Class<T> getType() {
218 return delegate.getType();
219 }
220
221 @Override
222 public void setType(Class<T> type) {
223 delegate.setType(type);
224 }
225
226 @Override
227 public String getName() {
228 return delegate.getName();
229 }
230
231 @Override
232 public void setName(String name) {
233 delegate.setName(name);
234 }
235
236 @Override
237 public boolean isReadonly() {
238 return delegate.isReadonly();
239 }
240
241 @Override
242 public void setReadonly(boolean isReadonly) {
243 delegate.setReadonly(isReadonly);
244 }
245
246 @Override
247 public boolean isI18n() {
248 return delegate.isI18n();
249 }
250
251 @Override
252 public void setI18n(boolean isI18n) {
253 delegate.setI18n(isI18n);
254 }
255
256 @Override
257 public Locale getLocale() {
258 return delegate.getLocale();
259 }
260
261 @Override
262 public void setLocale(Locale locale) {
263 delegate.setLocale(locale);
264 }
265
266 @Override
267 public T getDefaultValue() {
268 return delegate.getDefaultValue();
269 }
270
271 @Override
272 public void setDefaultValue(T defaultValue) {
273 delegate.setDefaultValue(defaultValue);
274 }
275
276 @Override
277 public PropertyNameDecorator getPropertyNameDecorator() {
278 return delegate.getPropertyNameDecorator();
279 }
280
281 @Override
282 public void setPropertyNameDecorator(PropertyNameDecorator propertyNameDecorator) {
283 delegate.setPropertyNameDecorator(propertyNameDecorator);
284 }
285 }
286
287
288
289
290
291
292
293
294
295 class LocalisedJcrPropertyDescriptor<T> extends JcrPropertyDescriptorWrapper<T> {
296
297 private final Node relatedNode;
298
299 LocalisedJcrPropertyDescriptor(JcrPropertyDescriptor<T> delegate, Node relatedNode) {
300 super(delegate);
301 this.relatedNode = relatedNode;
302 }
303
304 @Override
305 public String getName() {
306 return getName(relatedNode);
307 }
308
309 public String getName(Node node) {
310 if (locale != null &&
311 isI18n() &&
312 !genericLocalisationSupport.isDefaultLocale(locale, node)) {
313 return genericLocalisationSupport.deriveLocalisedPropertyName(super.getName(), locale);
314 }
315 return super.getName();
316 }
317 }
318
319
320
321
322
323
324
325
326 private class JcrItemPropertyDefinition<V> implements PropertyDefinition<Item, V> {
327
328 private final PropertySet<Item> propertySet;
329 private final JcrItemPropertySet.JcrPropertyDescriptor<V> descriptor;
330
331 private JcrItemPropertyDefinition(PropertySet<Item> propertySet, JcrPropertyDescriptor<V> descriptor) {
332 this.propertySet = propertySet;
333 this.descriptor = descriptor;
334 }
335
336 @Override
337 public ValueProvider<Item, V> getGetter() {
338 return item -> {
339 JcrPropertyDescriptor<V> descriptor = this.descriptor;
340 if (item.isNode()) {
341 descriptor = new LocalisedJcrPropertyDescriptor<>(descriptor, (Node) item);
342 }
343 return JcrItemInteractionStrategy.get(item).get(item, descriptor);
344 };
345 }
346
347 @Override
348 public Optional<Setter<Item, V>> getSetter() {
349 return Optional.of((Setter<Item, V>) (Item item, V value) -> {
350 JcrPropertyDescriptor<V> descriptor = this.descriptor;
351 if (item.isNode()) {
352 descriptor = new LocalisedJcrPropertyDescriptor<>(descriptor, (Node) item);
353 }
354
355 boolean isPropertyUpdatePermitted = !descriptor.isReadonly;
356
357 if (!isPropertyUpdatePermitted && item.isNode()) {
358 Node asNode = (Node) item;
359 String propertyName = descriptor.getName();
360
361 isPropertyUpdatePermitted = !Exceptions.wrap().get(() -> asNode.hasProperty(propertyName));
362 }
363
364 if (isPropertyUpdatePermitted) {
365 JcrItemInteractionStrategy.get(item).set(item, value, descriptor);
366 }
367 });
368 }
369
370 @Override
371 public Class<V> getType() {
372 return descriptor.getType();
373 }
374
375 @Override
376 public Class<?> getPropertyHolderType() {
377 return Node.class;
378 }
379
380 @Override
381 public String getName() {
382 return descriptor.getName();
383 }
384
385 @Override
386 public String getCaption() {
387 return descriptor.getName();
388 }
389
390 @Override
391 public PropertySet<Item> getPropertySet() {
392 return propertySet;
393 }
394 }
395 }