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.vaadin.integration.jcr;
35
36 import info.magnolia.cms.core.Path;
37 import info.magnolia.jcr.RuntimeRepositoryException;
38 import info.magnolia.jcr.util.NodeUtil;
39 import info.magnolia.jcr.util.PropertyUtil;
40
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.LinkedHashMap;
44 import java.util.LinkedList;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Map.Entry;
48
49 import javax.jcr.Item;
50 import javax.jcr.Node;
51 import javax.jcr.NodeIterator;
52 import javax.jcr.RepositoryException;
53
54 import org.apache.commons.lang3.StringUtils;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import com.vaadin.data.Property;
59
60
61
62
63
64 public abstract class AbstractJcrNodeAdapter extends AbstractJcrAdapter {
65
66 private static final Logger log = LoggerFactory.getLogger(AbstractJcrNodeAdapter.class);
67
68 private String primaryNodeType;
69
70 private final Map<String, AbstractJcrNodeAdapter> children = new LinkedHashMap<String, AbstractJcrNodeAdapter>();
71
72 private final Map<String, AbstractJcrNodeAdapter> removedChildren = new HashMap<String, AbstractJcrNodeAdapter>();
73
74 private AbstractJcrNodeAdapter parent;
75
76 private String nodeName;
77
78 private boolean childItemChanges = false;
79
80 public AbstractJcrNodeAdapter(Node jcrNode) {
81 super(jcrNode);
82 }
83
84 @Override
85 public boolean isNode() {
86 return true;
87 }
88
89 @Override
90 protected void initCommonAttributes(Item jcrItem) {
91 super.initCommonAttributes(jcrItem);
92 Node node = (Node) jcrItem;
93 try {
94 if (StringUtils.isBlank(primaryNodeType)) {
95 primaryNodeType = node.getPrimaryNodeType().getName();
96 }
97 } catch (RepositoryException e) {
98 log.error("Could not determine primaryNodeType name of JCR node", e);
99 primaryNodeType = UNIDENTIFIED;
100 }
101 }
102
103 protected void setPrimaryNodeTypeName(String primaryNodeTypeName) {
104 this.primaryNodeType = primaryNodeTypeName;
105 }
106
107
108
109
110
111
112 public String getPrimaryNodeTypeName() {
113 return primaryNodeType;
114 }
115
116 protected Map<String, AbstractJcrNodeAdapter> getRemovedChildren() {
117 return removedChildren;
118 }
119
120
121
122
123
124
125
126 @Override
127 public Node getJcrItem() {
128 return (Node)super.getJcrItem();
129 }
130
131
132
133
134 @Override
135 public boolean addItemProperty(Object id, Property property) {
136
137 log.debug("Add new Property Item name " + id + " with value " + property.getValue());
138 try {
139 Node node = getJcrItem();
140 String propertyName = (String) id;
141 if (!node.hasProperty(propertyName)) {
142
143 node.setProperty(propertyName, (String) property.getValue());
144 getChangedProperties().put((String)id, property);
145 return true;
146 } else {
147 log.warn("Property " + id + " already exist.");
148 return false;
149 }
150 } catch (RepositoryException e) {
151 log.error("Unable to add JCR property", e);
152 return false;
153 }
154 }
155
156
157
158
159
160 @Override
161 public Property getItemProperty(Object id) {
162 Object value;
163 Class type = String.class;
164 try {
165 final Node jcrNode = getJcrItem();
166 if (!jcrNode.hasProperty((String) id)) {
167 if (ModelConstants.JCR_NAME.equals(id)) {
168 value = jcrNode.getName();
169 } else {
170 return null;
171 }
172 } else {
173 value = PropertyUtil.getPropertyValueObject(jcrNode, String.valueOf(id));
174 type = value.getClass();
175 }
176 } catch (RepositoryException e) {
177 throw new RuntimeRepositoryException(e);
178 }
179 DefaultProperty property = new DefaultProperty(type, value);
180 getChangedProperties().put((String) id, property);
181 return property;
182 }
183
184
185
186
187
188
189 @Override
190 public Node applyChanges() throws RepositoryException {
191
192 Node node = getJcrItem();
193
194
195 updateProperties(node);
196 updateChildren(node);
197
198 return node;
199 }
200
201
202
203
204
205
206 public void updateChildren(Node node) throws RepositoryException {
207 if (!children.isEmpty()) {
208 List<String> sortedChildIdentifiers = new LinkedList<>();
209 for (AbstractJcrNodeAdapter child : children.values()) {
210
211 child.applyChanges();
212 sortedChildIdentifiers.add(child.getJcrItem().getIdentifier());
213 }
214 if (node.getPrimaryNodeType().hasOrderableChildNodes()) {
215 sortChildren(node, sortedChildIdentifiers);
216 }
217 }
218
219
220 if (!removedChildren.isEmpty()) {
221 for (AbstractJcrNodeAdapter removedChild : removedChildren.values()) {
222 if (node.hasNode(removedChild.getNodeName())) {
223 node.getNode(removedChild.getNodeName()).remove();
224 }
225 }
226 }
227 }
228
229
230
231
232 private void sortChildren(Node node, List<String> sortedIdentifiers) throws RepositoryException {
233
234 List<String> unsortedIdentifiers = new ArrayList<>();
235
236 for (NodeIterator it = node.getNodes(); it.hasNext();) {
237 unsortedIdentifiers.add(it.nextNode().getIdentifier());
238 }
239
240 for (int pos = 0; pos < sortedIdentifiers.size(); pos++) {
241 String current = sortedIdentifiers.get(pos);
242 int nodePos = unsortedIdentifiers.indexOf(current);
243
244 if (nodePos != -1 && nodePos != pos) {
245 Node nodeToMove = node.getSession().getNodeByIdentifier(current);
246 Node target = node.getSession().getNodeByIdentifier(unsortedIdentifiers.get(pos));
247
248 NodeUtil.moveNodeBefore(nodeToMove, target);
249 String movedId = unsortedIdentifiers.remove(nodePos);
250 unsortedIdentifiers.add(pos, movedId);
251 }
252 }
253 }
254
255 @Override
256 public void updateProperties(Item item) throws RepositoryException {
257 super.updateProperties(item);
258 if (item instanceof Node) {
259
260 Node node = (Node) item;
261 for (Entry<String, Property> entry : getRemovedProperties().entrySet()) {
262 if (node.hasProperty(entry.getKey())) {
263 node.getProperty(entry.getKey()).remove();
264 }
265 }
266 getRemovedProperties().clear();
267 }
268 }
269
270
271
272
273
274
275
276
277
278
279 @Override
280 protected void updateProperty(Item item, String propertyId, Property property) {
281 if (!(item instanceof Node)) {
282 return;
283 }
284 Node node = (Node) item;
285 if (ModelConstants.JCR_NAME.equals(propertyId)) {
286 String jcrName = (String) property.getValue();
287 try {
288 if (jcrName != null && !jcrName.isEmpty() && !jcrName.equals(node.getName())) {
289
290
291 jcrName = Path.getValidatedLabel(jcrName);
292 jcrName = Path.getUniqueLabel(node.getSession(), node.getParent().getPath(), jcrName);
293
294 NodeUtil.renameNode(node, jcrName);
295
296 setItemId(JcrItemUtil.getItemId(node));
297 }
298 } catch (RepositoryException e) {
299 log.error("Could not rename JCR Node.", e);
300 }
301 } else if (propertyId != null && !propertyId.isEmpty()) {
302 if (property.getValue() != null) {
303 try {
304 PropertyUtil.setProperty(node, propertyId, property.getValue());
305 } catch (RepositoryException e) {
306 log.error("Could not set JCR Property {}", propertyId, e);
307 }
308 } else {
309 removeItemProperty(propertyId);
310 log.debug("Property '{}' has a null value: Will be removed", propertyId);
311 }
312 }
313 }
314
315
316
317
318
319 public AbstractJcrNodeAdapter getChild(String nodeName) {
320 return children.get(nodeName);
321 }
322
323 public Map<String, AbstractJcrNodeAdapter> getChildren() {
324 return children;
325 }
326
327
328
329
330
331 public AbstractJcrNodeAdapter addChild(AbstractJcrNodeAdapter child) {
332 childItemChanges = true;
333 if (removedChildren.containsKey(child.getNodeName())) {
334 removedChildren.remove(child.getNodeName());
335 }
336 child.setParent(this);
337 if (children.containsKey(child.getNodeName())) {
338 children.remove(child.getNodeName());
339 }
340 return children.put(child.getNodeName(), child);
341 }
342
343
344
345
346
347
348 public boolean removeChild(AbstractJcrNodeAdapter toRemove) {
349 childItemChanges = true;
350 removedChildren.put(toRemove.getNodeName(), toRemove);
351 return children.remove(toRemove.getNodeName()) != null;
352 }
353
354
355
356
357 public AbstractJcrNodeAdapter getParent() {
358 return parent;
359 }
360
361 public void setParent(AbstractJcrNodeAdapter parent) {
362 this.parent = parent;
363 }
364
365
366
367
368
369 public String getNodeName() {
370 return this.nodeName;
371 }
372
373 public void setNodeName(String nodeName) {
374 this.nodeName = nodeName;
375 }
376
377
378
379
380 public boolean hasChildItemChanges() {
381 return childItemChanges;
382 }
383 }