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.transformer.multi;
35
36 import info.magnolia.jcr.util.NodeTypes;
37 import info.magnolia.jcr.util.NodeUtil;
38 import info.magnolia.jcr.util.PropertyUtil;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
41 import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
42 import info.magnolia.ui.form.field.transformer.basic.BasicTransformer;
43 import info.magnolia.ui.vaadin.integration.NullItem;
44 import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeAdapter;
45 import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
46
47 import java.text.DecimalFormat;
48 import java.util.ArrayList;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Set;
53
54 import javax.inject.Inject;
55 import javax.jcr.Node;
56 import javax.jcr.RepositoryException;
57
58 import org.apache.jackrabbit.commons.predicate.Predicate;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.vaadin.v7.data.Item;
63 import com.vaadin.v7.data.Property;
64 import com.vaadin.v7.data.util.ObjectProperty;
65 import com.vaadin.v7.data.util.PropertysetItem;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class MultiValueChildrenNodeTransformer extends BasicTransformer<PropertysetItem> {
83
84 private static final Logger log = LoggerFactory.getLogger(MultiValueChildrenNodeTransformer.class);
85
86 protected String childNodeType = NodeTypes.ContentNode.NAME;
87 private String childValuePropertyName;
88
89 @Inject
90 public MultiValueChildrenNodeTransformer(Item relatedFormItem, ConfiguredFieldDefinition definition, Class<PropertysetItem> type, I18NAuthoringSupport i18NAuthoringSupport) {
91 super(relatedFormItem, definition, type, i18NAuthoringSupport);
92 this.childValuePropertyName = definition.getName();
93 }
94
95
96
97
98 @Deprecated
99 public MultiValueChildrenNodeTransformer(Item relatedFormItem, ConfiguredFieldDefinition definition, Class<PropertysetItem> type) {
100 this(relatedFormItem, definition, type, Components.getComponent(I18NAuthoringSupport.class));
101 }
102
103
104
105
106 @Override
107 public boolean hasI18NSupport() {
108 return false;
109 }
110
111
112
113
114
115
116
117 @Override
118 public PropertysetItem readFromItem() {
119 PropertysetItem newValues = new PropertysetItem();
120 JcrNodeAdapter rootItem = getRootItem();
121
122 List<Node> childNodes = getStoredChildNodes(rootItem);
123 int position = 0;
124 for (Node child : childNodes) {
125 Object value = getValueFromChildNode(child);
126 if (value != null) {
127 newValues.addItemProperty(position, new ObjectProperty<>(value));
128 position += 1;
129 }
130 }
131 return newValues;
132 }
133
134
135
136
137
138
139
140 @Override
141 public void writeToItem(PropertysetItem newValue) {
142
143 JcrNodeAdapter rootItem = getRootItem();
144 if (rootItem == null) {
145
146 return;
147 }
148 rootItem.getChildren().clear();
149
150 setNewChildItem(rootItem, newValue);
151
152 detachNoMoreExistingChildren(rootItem);
153
154 handleRootitemAndParent(rootItem);
155 }
156
157
158
159
160 protected JcrNodeAdapter getRootItem() {
161 return (JcrNodeAdapter) relatedFormItem;
162 }
163
164
165
166
167
168 protected List<Node> getStoredChildNodes(JcrNodeAdapter parent) {
169 List<Node> res = new ArrayList<>();
170 try {
171 if (parent != null && !(parent instanceof JcrNewNodeAdapter) && parent.getJcrItem().hasNodes()) {
172 Predicate predicate = createPredicateToEvaluateChildNode();
173 if (predicate != null) {
174 res = NodeUtil.asList(NodeUtil.getNodes(parent.getJcrItem(), predicate));
175 } else {
176 res = NodeUtil.asList(NodeUtil.getNodes(parent.getJcrItem(), childNodeType));
177 }
178 }
179 } catch (RepositoryException re) {
180 log.warn("Not able to access the Child Nodes of the following Node Identifier {}", parent.getItemId(), re);
181 }
182 return res;
183 }
184
185
186
187
188
189 protected Predicate createPredicateToEvaluateChildNode() {
190
191 return node -> {
192 if (node instanceof Node) {
193 try {
194 return ((Node) node).getName().matches("[0-9]+");
195 } catch (RepositoryException e) {
196 return false;
197 }
198 }
199 return false;
200 };
201 }
202
203
204
205
206 protected Object getValueFromChildNode(Node child) {
207 try {
208 if (child.hasProperty(childValuePropertyName)) {
209 return PropertyUtil.getPropertyValueObject(child, childValuePropertyName);
210 }
211 } catch (RepositoryException re) {
212 log.warn("Not able to access the Child Nodes property of the following Child Node Name {}", NodeUtil.getName(child), re);
213 }
214 return null;
215 }
216
217
218 protected void setNewChildItem(JcrNodeAdapter rootItem, PropertysetItem newValue) {
219
220 Set<String> childNames = new HashSet<>();
221 Node rootNode = rootItem.getJcrItem();
222 try {
223 Iterator<?> it = newValue.getItemPropertyIds().iterator();
224 while (it.hasNext()) {
225 Property<?> p = newValue.getItemProperty(it.next());
226
227 if (p == null || p.getValue() == null) {
228 continue;
229 }
230 Object value = p.getValue();
231
232 String childName = createChildItemName(childNames, value, rootItem);
233
234 JcrNodeAdapter childItem = initializeChildItem(rootItem, rootNode, childName);
235
236 setChildItemValue(childItem, value);
237 }
238 } catch (Exception e) {
239 log.warn("Not able to create a Child Item for {} ", rootItem.getItemId(), e);
240 }
241 }
242
243
244
245
246 protected void setChildItemValue(JcrNodeAdapter childItem, Object value) {
247 childItem.addItemProperty(childValuePropertyName, new ObjectProperty<>(value));
248 }
249
250
251
252
253
254
255 protected JcrNodeAdapter initializeChildItem(JcrNodeAdapter rootItem, Node rootNode, String childName) throws RepositoryException {
256 JcrNodeAdapter childItem;
257 if (!(rootItem instanceof JcrNewNodeAdapter) && rootNode.hasNode(childName)) {
258 childItem = new JcrNodeAdapter(rootNode.getNode(childName));
259 } else {
260 childItem = new JcrNewNodeAdapter(rootNode, childNodeType, childName);
261 }
262 rootItem.addChild(childItem);
263 return childItem;
264 }
265
266
267
268
269 private void detachNoMoreExistingChildren(JcrNodeAdapter rootItem) {
270 try {
271 List<Node> children = getStoredChildNodes(rootItem);
272 for (Node child : children) {
273 if (rootItem.getChild(child.getName()) == null) {
274 JcrNodeAdapter toRemove = new JcrNodeAdapter(child);
275 rootItem.removeChild(toRemove);
276 } else {
277 detachNoMoreExistingChildren((JcrNodeAdapter) rootItem.getChild(child.getName()));
278 }
279 }
280 } catch (RepositoryException e) {
281 log.error("Could remove children", e);
282 }
283 }
284
285
286
287
288
289
290
291
292 protected void handleRootitemAndParent(JcrNodeAdapter rootItem) {
293
294 }
295
296
297
298
299 protected String createChildItemName(Set<String> childNames, Object value, JcrNodeAdapter rootItem) {
300 int nb = 0;
301 String name = "00";
302 DecimalFormat df = new DecimalFormat("00");
303 while (childNames.contains(name)) {
304 nb += 1;
305 name = df.format(nb);
306 }
307 childNames.add(name);
308 return name;
309 }
310
311 public void setChildValuePropertyName(String newName) {
312 this.childValuePropertyName = newName;
313 }
314
315
316
317
318 protected JcrNodeAdapter getOrCreateChildNode(String childNodeName, String childNodeType) throws RepositoryException {
319 JcrNodeAdapter child;
320 if (relatedFormItem instanceof NullItem) {
321 return null;
322 }
323 if (!(relatedFormItem instanceof JcrNodeAdapter)) {
324 log.warn("Detected attempt to retrieve a Jcr Item from a Non Jcr Item Adapter. Will return null.");
325 return null;
326 }
327 Node node = ((JcrNodeAdapter) relatedFormItem).getJcrItem();
328 if (node.hasNode(childNodeName) && !(relatedFormItem instanceof JcrNewNodeAdapter)) {
329 child = new JcrNodeAdapter(node.getNode(childNodeName));
330 ((JcrNodeAdapter) relatedFormItem).addChild(child);
331 } else {
332 child = new JcrNewNodeAdapter(node, childNodeType, childNodeName);
333 ((JcrNodeAdapter) relatedFormItem).addChild(child);
334 }
335 return child;
336 }
337 }