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.jcr.inheritance;
35
36 import info.magnolia.jcr.RuntimeRepositoryException;
37 import info.magnolia.jcr.decoration.AbstractContentDecorator;
38 import info.magnolia.jcr.decoration.ContentDecoratorNodeWrapper;
39 import info.magnolia.jcr.iterator.ChainedNodeIterator;
40 import info.magnolia.jcr.iterator.FilteringNodeIterator;
41 import info.magnolia.jcr.iterator.RangeIteratorImpl;
42 import info.magnolia.jcr.predicate.AbstractPredicate;
43 import info.magnolia.jcr.util.NodeUtil;
44 import info.magnolia.jcr.wrapper.DelegateNodeWrapper;
45
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.List;
52
53 import javax.jcr.Node;
54 import javax.jcr.NodeIterator;
55 import javax.jcr.PathNotFoundException;
56 import javax.jcr.Property;
57 import javax.jcr.PropertyIterator;
58 import javax.jcr.RepositoryException;
59
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 public class InheritanceContentDecorator extends AbstractContentDecorator implements Cloneable {
78
79 private static final Logger log = LoggerFactory.getLogger(InheritanceContentDecorator.class);
80
81
82 private final Node destination;
83
84
85 private List<Node> sources = new ArrayList<Node>();
86
87 private AbstractPredicate<Node> childInheritancePredicate = new AbstractPredicate<Node>() {
88 @Override
89 public boolean evaluateTyped(Node node) {
90 try {
91 return isSourceChildInherited(node);
92 } catch (RepositoryException e) {
93 throw new RuntimeRepositoryException(e);
94 }
95 }
96 };
97
98 public InheritanceContentDecorator(Node destination) throws RepositoryException {
99 this.destination = destination;
100 }
101
102 @Override
103 public Node wrapNode(Node node) {
104 try {
105 if (NodeUtil.isSame(destination, node)) {
106 return new DestinationNodeInheritanceNodeWrapper(node);
107 } else {
108 return new OtherNodeInheritanceNodeWrapper(node);
109 }
110 } catch (RepositoryException e) {
111 throw new RuntimeRepositoryException(e);
112 }
113 }
114
115 public Node getDestination() {
116 return destination;
117 }
118
119
120
121
122 private AbstractPredicate<Node> getChildInheritancePredicate() {
123
124 if (childInheritancePredicate != null) {
125 return childInheritancePredicate;
126 }
127
128 childInheritancePredicate = new AbstractPredicate<Node>() {
129 @Override
130 public boolean evaluateTyped(Node node) {
131 try {
132 return isSourceChildInherited(node);
133 } catch (RepositoryException e) {
134 throw new RuntimeRepositoryException(e);
135 }
136 }
137 };
138
139 return childInheritancePredicate;
140 }
141
142 public void addSource(Node source) {
143 this.getSources().add(source);
144 }
145
146
147
148
149
150
151
152
153 protected boolean inheritsNodes(Node node) throws RepositoryException {
154 return true;
155 }
156
157
158
159
160
161
162
163
164 protected boolean inheritsProperties(Node node) throws RepositoryException {
165 return true;
166 }
167
168
169
170
171
172
173
174
175 protected boolean isSourceChildInherited(Node node) throws RepositoryException {
176 return true;
177 }
178
179
180
181
182
183
184
185
186
187
188 protected NodeIterator sortInheritedNodes(NodeIterator destinationChildren, List<NodeIterator> sourceChildren) throws RepositoryException {
189 Collections.reverse(sourceChildren);
190 sourceChildren.add(destinationChildren);
191 return new FilteringNodeIterator(new ChainedNodeIterator(sourceChildren), getChildInheritancePredicate());
192 }
193
194
195
196
197
198
199
200
201
202
203
204 protected PropertyIterator combinePropertyIterators(PropertyIterator destinationProperties, List<PropertyIterator> sourceProperties) throws RepositoryException {
205 HashSet<String> names = new HashSet<String>();
206 ArrayList<Property> properties = new ArrayList<Property>();
207 while (destinationProperties.hasNext()) {
208 Property property = destinationProperties.nextProperty();
209 names.add(property.getName());
210 properties.add(property);
211 }
212 for (PropertyIterator propertyIterator : sourceProperties) {
213 while (propertyIterator.hasNext()) {
214 Property property = (Property) propertyIterator.next();
215 if (!names.contains(property.getName())) {
216 names.add(property.getName());
217 properties.add(property);
218 }
219 }
220 }
221 return new PropertyIteratorImpl(properties);
222 }
223
224
225
226
227
228
229 private class OtherNodeInheritanceNodeWrapper extends ContentDecoratorNodeWrapper implements InheritanceNodeWrapper {
230
231 public OtherNodeInheritanceNodeWrapper(Node node) {
232 super(node, InheritanceContentDecorator.this);
233 }
234
235 @Override
236 public boolean isInherited() {
237 try {
238
239 Node wrappedNode = getWrappedNode();
240
241 if (isChildOf(wrappedNode, destination)) {
242 return false;
243 }
244
245 Node n = destination;
246 Iterator<Node> iterator = getSources().iterator();
247 while (iterator.hasNext() && inheritsNodes(n)) {
248 n = iterator.next();
249 if (isChildOf(wrappedNode, n) && isSourceChildInherited(wrappedNode)) {
250 return true;
251 }
252 }
253
254 return false;
255 } catch (RepositoryException e) {
256 throw new RuntimeRepositoryException(e);
257 }
258 }
259
260 private boolean isChildOf(Node child, Node parent) {
261 try {
262 return parent.getDepth() == 0 || child.getPath().startsWith(parent.getPath() + "/");
263 } catch (RepositoryException e) {
264 throw new RuntimeRepositoryException(e);
265 }
266 }
267 }
268
269
270
271
272
273
274 public class DestinationNodeInheritanceNodeWrapper extends ContentDecoratorNodeWrapper implements InheritanceNodeWrapper {
275
276 public DestinationNodeInheritanceNodeWrapper(Node node) {
277 super(node, InheritanceContentDecorator.this);
278 }
279
280 @Override
281 public boolean hasNode(String relPath) throws RepositoryException {
282 if (super.hasNode(relPath)) {
283 return true;
284 }
285 Node current = getWrappedNode();
286 Iterator<Node> iterator = getSources().iterator();
287 while (iterator.hasNext() && inheritsNodes(current)) {
288 current = iterator.next();
289 if (current.hasNode(relPath) && isSourceChildInherited(current.getNode(relPath))) {
290 return true;
291 }
292 }
293 return false;
294 }
295
296 @Override
297 public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
298 if (super.hasNode(relPath)) {
299 return super.getNode(relPath);
300 }
301 Node current = getWrappedNode();
302 Iterator<Node> iterator = getSources().iterator();
303 while (iterator.hasNext() && inheritsNodes(current)) {
304 current = iterator.next();
305 if (current.hasNode(relPath)) {
306 Node node = current.getNode(relPath);
307 if (isSourceChildInherited(node)) {
308 return wrapNode(node);
309 }
310 }
311 }
312 throw new PathNotFoundException(relPath);
313 }
314
315 @Override
316 public NodeIterator getNodes() throws RepositoryException {
317 List<NodeIterator> nodes = new ArrayList<NodeIterator>();
318 Node current = getWrappedNode();
319 Iterator<Node> iterator = getSources().iterator();
320 while (iterator.hasNext() && inheritsNodes(current)) {
321 current = iterator.next();
322 nodes.add(current.getNodes());
323 }
324 return super.wrapNodeIterator(sortInheritedNodes(getWrappedNode().getNodes(), nodes));
325 }
326
327 @Override
328 public NodeIterator getNodes(String namePattern) throws RepositoryException {
329 List<NodeIterator> nodes = new ArrayList<NodeIterator>();
330 Node current = getWrappedNode();
331 Iterator<Node> iterator = getSources().iterator();
332 while (iterator.hasNext() && inheritsNodes(current)) {
333 current = iterator.next();
334 nodes.add(current.getNodes(namePattern));
335 }
336 return super.wrapNodeIterator(sortInheritedNodes(getWrappedNode().getNodes(namePattern), nodes));
337 }
338
339 @Override
340 public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
341 List<NodeIterator> nodes = new ArrayList<NodeIterator>();
342 Node current = getWrappedNode();
343 Iterator<Node> iterator = getSources().iterator();
344 while (iterator.hasNext() && inheritsNodes(current)) {
345 current = iterator.next();
346 nodes.add(current.getNodes(nameGlobs));
347 }
348 return super.wrapNodeIterator(sortInheritedNodes(getWrappedNode().getNodes(nameGlobs), nodes));
349 }
350
351 @Override
352 public boolean hasProperty(String relPath) throws RepositoryException {
353 if (super.hasProperty(relPath)) {
354 return true;
355 }
356 Node current = getWrappedNode();
357 Iterator<Node> iterator = getSources().iterator();
358 while (iterator.hasNext() && inheritsProperties(current)) {
359 current = iterator.next();
360 if (current.hasProperty(relPath)) {
361 return true;
362 }
363 }
364 return false;
365 }
366
367 @Override
368 public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
369 if (super.hasProperty(relPath)) {
370 return super.getProperty(relPath);
371 }
372 Node current = getWrappedNode();
373 Iterator<Node> iterator = getSources().iterator();
374 while (iterator.hasNext() && inheritsProperties(current)) {
375 current = iterator.next();
376 if (current.hasProperty(relPath)) {
377 return wrapProperty(current.getProperty(relPath));
378 }
379 }
380 throw new PathNotFoundException(relPath);
381 }
382
383 @Override
384 public PropertyIterator getProperties() throws RepositoryException {
385 ArrayList<PropertyIterator> properties = new ArrayList<PropertyIterator>();
386 Node current = getWrappedNode();
387 Iterator<Node> iterator = getSources().iterator();
388 while (iterator.hasNext() && inheritsProperties(current)) {
389 current = iterator.next();
390 properties.add(current.getProperties());
391 }
392 return super.wrapPropertyIterator(combinePropertyIterators(super.getProperties(), properties));
393 }
394
395 @Override
396 public PropertyIterator getProperties(String namePattern) throws RepositoryException {
397 ArrayList<PropertyIterator> properties = new ArrayList<PropertyIterator>();
398 Node current = getWrappedNode();
399 Iterator<Node> iterator = getSources().iterator();
400 while (iterator.hasNext() && inheritsProperties(current)) {
401 current = iterator.next();
402 properties.add(current.getProperties(namePattern));
403 }
404 return super.wrapPropertyIterator(combinePropertyIterators(super.getProperties(namePattern), properties));
405 }
406
407 @Override
408 public PropertyIterator getProperties(String[] nameGlobs) throws RepositoryException {
409 ArrayList<PropertyIterator> properties = new ArrayList<PropertyIterator>();
410 Node current = getWrappedNode();
411 Iterator<Node> iterator = getSources().iterator();
412 while (iterator.hasNext() && inheritsProperties(current)) {
413 current = iterator.next();
414 properties.add(current.getProperties(nameGlobs));
415 }
416 return super.wrapPropertyIterator(combinePropertyIterators(super.getProperties(nameGlobs), properties));
417 }
418
419 @Override
420 public boolean isInherited() {
421 return false;
422 }
423
424 @Override
425 public Node deepUnwrap(Class<? extends DelegateNodeWrapper> wrapper) {
426
427 if (DestinationNodeInheritanceNodeWrapper.class.equals(wrapper)) {
428 return super.deepUnwrap(wrapper);
429 }
430 try {
431 InheritanceContentDecorator decorator = (InheritanceContentDecorator) ((InheritanceContentDecorator) this.getContentDecorator()).clone();
432 List<Node> unwrappedSources = new ArrayList<Node>();
433
434 Iterator<Node> iterator = decorator.getSources().iterator();
435 while (iterator.hasNext()) {
436 Node source = iterator.next();
437 if (source instanceof DelegateNodeWrapper) {
438 source = ((DelegateNodeWrapper) source).deepUnwrap(wrapper);
439 }
440 unwrappedSources.add(source);
441 }
442 decorator.setSources(unwrappedSources);
443 return decorator.wrapNode(((DelegateNodeWrapper) decorator.getDestination()).deepUnwrap(wrapper));
444
445 } catch (CloneNotSupportedException e) {
446
447 log.error("Failed to clone itself with " + e.getLocalizedMessage(), e);
448 return null;
449 }
450 }
451 }
452
453 private static class PropertyIteratorImpl extends RangeIteratorImpl implements PropertyIterator {
454
455 public PropertyIteratorImpl(Collection<Property> collection) {
456 super(collection);
457 }
458
459 @Override
460 public Property nextProperty() {
461 return (Property) super.next();
462 }
463 }
464
465 @Override
466 public boolean isMultipleWrapEnabled() {
467 return true;
468 }
469
470 private List<Node> getSources() {
471 return sources;
472 }
473
474 private void setSources(List<Node> sources) {
475 this.sources = sources;
476 }
477 }