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.databinding;
35
36 import static java.util.stream.Collectors.*;
37
38 import info.magnolia.jcr.RuntimeRepositoryException;
39 import info.magnolia.jcr.util.NodeNameHelper;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.jcr.util.PropertyUtil;
42 import info.magnolia.objectfactory.Components;
43 import info.magnolia.ui.databinding.JcrItemPropertySet.JcrPropertyDescriptor;
44 import info.magnolia.util.CollectionConversionCapableBeanUtils;
45 import info.magnolia.util.JcrValueConverter;
46
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collection;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.Optional;
53 import java.util.Set;
54 import java.util.stream.Stream;
55
56 import javax.jcr.InvalidItemStateException;
57 import javax.jcr.Item;
58 import javax.jcr.ItemNotFoundException;
59 import javax.jcr.Node;
60 import javax.jcr.Property;
61 import javax.jcr.RepositoryException;
62 import javax.jcr.Value;
63
64 import org.apache.commons.beanutils.BeanUtilsBean;
65 import org.apache.commons.beanutils.ConversionException;
66 import org.apache.commons.lang3.StringUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import lombok.SneakyThrows;
71
72
73
74
75
76
77
78
79 abstract class JcrItemInteractionStrategyImpl<I extends Item> implements JcrItemInteractionStrategy<I> {
80
81 private static final Logger log = LoggerFactory.getLogger(JcrItemInteractionStrategy.class);
82
83 private BeanUtilsBean beanUtils = new CollectionConversionCapableBeanUtils();
84
85 @SneakyThrows(RepositoryException.class)
86 <V> V getPropertyValue(Property property, Class<V> type) {
87 try {
88 boolean multiValue = property.isMultiple();
89
90 if (multiValue) {
91
92 final Value[] values = property.getValues();
93 final Set<Object> unpackedValues =
94 Stream.of(values)
95 .map(JcrValueConverter::read)
96 .collect(toSet());
97
98
99
100 return (V) beanUtils.getConvertUtils().convert(unpackedValues, type);
101 } else {
102 return convertToPresentation(JcrValueConverter.read(property.getValue()), type).orElse(null);
103 }
104 } catch (InvalidItemStateException | ItemNotFoundException e) {
105 log.debug("Failed to get the value of property [{}], returning null", property, e);
106 return null;
107 }
108 }
109
110 <V> void setPropertyValue(Node node, String name, V value) {
111 try {
112 if ("jcrName".equals(name)) {
113 handleJcrItemNameChange(node, String.valueOf(value));
114 }
115 if (node.hasProperty(name)) {
116 if (propertyValueEquals(node.getProperty(name), value)) {
117 return;
118 }
119 }
120
121 PropertyUtil.setProperty(node, name, value);
122 } catch (RepositoryException e) {
123 throw new RuntimeRepositoryException(e);
124 }
125 }
126
127 private <V> boolean propertyValueEquals(Property property, V value) throws RepositoryException {
128 if (!property.isMultiple()) {
129 return Objects.equals(JcrValueConverter.read(property.getValue()), value);
130 } else {
131 final Value[] values = property.getValues();
132 final List<Object> jcrValues = Arrays.stream(values).map(JcrValueConverter::read).collect(toList());
133 return value instanceof Collection && Objects.equals(jcrValues, new ArrayList<>((Collection<?>) value));
134 }
135 }
136
137 protected <V> Optional<V> convertToPresentation(Object value, Class<V> targetType) {
138 try {
139
140 return Optional.ofNullable((V) beanUtils.getConvertUtils().convert(value, targetType));
141 } catch (ConversionException e) {
142 return Optional.empty();
143 }
144 }
145
146
147
148
149 final static class WithNodes extends JcrItemInteractionStrategyImpl<Node> {
150
151 @Override
152 public <V> V get(Node node, JcrPropertyDescriptor<V> descriptor) {
153 final Property property = PropertyUtil.getPropertyOrNull(node, descriptor.getName());
154 if ("jcrName".equals(descriptor.getName()) && property == null) {
155
156 return (V) NodeUtil.getName(node);
157 }
158 return Optional.ofNullable(property)
159 .map(prop -> getPropertyValue(prop, descriptor.getType()))
160 .orElse(null);
161 }
162
163 @Override
164 public <V> void set(Node node, V value, JcrPropertyDescriptor<V> descriptor) {
165 if (notNullOrEmptyString(value)) {
166 setPropertyValue(node, descriptor.getName(), value);
167 } else {
168
169 Optional.ofNullable(PropertyUtil.getPropertyOrNull(node, descriptor.getName())).ifPresent(property -> {
170 try {
171 property.remove();
172 } catch (RepositoryException e) {
173 throw new RuntimeRepositoryException(e);
174 }
175 });
176 }
177 }
178
179 private boolean notNullOrEmptyString(Object value) {
180 if (value instanceof String) {
181 return StringUtils.isNotEmpty((String) value);
182 }
183 return value != null;
184 }
185 }
186
187
188
189
190 final static class WithProperties extends JcrItemInteractionStrategyImpl<Property> {
191
192 @Override
193 @SneakyThrows(RepositoryException.class)
194 public <V> V get(Property property, JcrPropertyDescriptor<V> descriptor) {
195 switch (descriptor.getName()) {
196 case "value":
197 return getPropertyValue(property, descriptor.getType());
198
199
200
201
202 case "jcrName":
203 case "name":
204
205 return (V) property.getName();
206 default:
207 return null;
208 }
209 }
210
211 @Override
212 public <V> void set(Property item, V value, JcrPropertyDescriptor<V> descriptor) {
213 try {
214 switch (descriptor.getName()) {
215 case "value":
216 setPropertyValue(item.getParent(), item.getName(), value);
217 break;
218 case "jcrName":
219 case "name":
220 handleJcrItemNameChange(item, String.valueOf(value));
221 }
222 } catch (RepositoryException e) {
223 log.warn("Failed to set the value [{}] to [{}]...", e);
224 }
225 }
226 }
227
228 private static final NodeNameHelper nodeNameHelper = Components.getComponent(NodeNameHelper.class);
229
230 private static void handleJcrItemNameChange(Item jcrItem, String pendingNewName) throws RepositoryException {
231 if (StringUtils.isNotEmpty(pendingNewName) && !Objects.equals(pendingNewName, jcrItem.getName())) {
232 final String sanitisedNewName = nodeNameHelper.getUniqueName(jcrItem.getParent(), pendingNewName);
233 if (jcrItem.isNode()) {
234 NodeUtil.renameNode((Node) jcrItem, sanitisedNewName);
235 } else {
236 PropertyUtil.renameProperty((Property) jcrItem, sanitisedNewName);
237 }
238 }
239 }
240 }