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