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