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.rest.delivery.jcr.decorator;
35
36 import info.magnolia.jcr.decoration.ContentDecoratorNodeWrapper;
37 import info.magnolia.jcr.util.NodeUtil;
38 import info.magnolia.objectfactory.ComponentProvider;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.rest.delivery.jcr.filter.FilteringContentDecoratorBuilder;
41 import info.magnolia.rest.reference.ReferenceDefinition;
42 import info.magnolia.rest.reference.ReferenceResolver;
43 import info.magnolia.rest.reference.ReferenceResolverDefinition;
44
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.HashMap;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Optional;
52 import java.util.stream.Collectors;
53
54 import javax.jcr.Item;
55 import javax.jcr.Node;
56 import javax.jcr.NodeIterator;
57 import javax.jcr.Property;
58 import javax.jcr.PropertyIterator;
59 import javax.jcr.RepositoryException;
60
61 import org.apache.jackrabbit.commons.ItemNameMatcher;
62 import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
63 import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
64 import org.apache.jackrabbit.spi.commons.iterator.Iterators;
65
66 import com.google.common.collect.Lists;
67
68
69
70
71 public class ReferenceExpandWrapper extends ContentDecoratorNodeWrapper {
72
73 private final Map<String, Item> resolvedItems = new HashMap<>();
74 private final FilteringContentDecoratorBuilder expandDecorators;
75 private final ComponentProvider componentProvider = Components.getComponentProvider();
76
77 public ReferenceExpandWrapper(Node wrapped, List<ReferenceDefinition> references, FilteringContentDecoratorBuilder expandDecorators) {
78 super(wrapped, new ReferenceExpandDecorator(references, expandDecorators));
79 this.expandDecorators = expandDecorators;
80 try {
81 PropertyIterator properties = wrapped.getProperties();
82 while (properties.hasNext()) {
83 Property property = properties.nextProperty();
84
85 Optional<Node> resolvedItem = resolveProperty(property, references);
86
87 if (resolvedItem.isPresent()) {
88 resolvedItems.put(property.getName(), resolvedItem.get());
89 }
90 }
91 } catch (RepositoryException e) {
92 throw new RuntimeException("Can't wrap node [" + wrapped + "]", e);
93 }
94 }
95
96 private Optional<Node> resolveProperty(Property property, List<ReferenceDefinition> references) throws RepositoryException {
97 for (ReferenceDefinition definition : references) {
98 boolean matchNodeType = definition.getNodeType() == null || definition.getNodeType() != null && NodeUtil.isNodeType(wrapped, definition.getNodeType());
99 boolean matchItemName = definition.getPropertyName() == null || property.getName().matches(definition.getPropertyName());
100 if (matchNodeType && matchItemName) {
101 Optional<Node> itemOptional = resolveReference(property, definition);
102 if (itemOptional.isPresent()) {
103 Node resolvedNode = itemOptional.get();
104 return Optional.of(new AdditionNodeWrapper(resolvedNode, property.getName()));
105 }
106 }
107 }
108
109 return Optional.empty();
110 }
111
112 private Optional<Node> resolveReference(Property property, ReferenceDefinition definition) throws RepositoryException {
113 ReferenceResolverDefinition resolverDefinition = definition.getReferenceResolver();
114 if (resolverDefinition != null && resolverDefinition.getImplementationClass() != null) {
115 ReferenceResolver referenceResolver = componentProvider.newInstance(resolverDefinition.getImplementationClass(), resolverDefinition);
116 Optional resolved = referenceResolver.resolve(property);
117 if (resolved.isPresent() && resolved.get() instanceof Node) {
118 return resolved;
119 }
120 }
121
122 return Optional.empty();
123 }
124
125 @Override
126 public boolean hasProperties() throws RepositoryException {
127 boolean hasProperties = resolvedItems.values().stream()
128 .anyMatch(item -> !item.isNode());
129 return hasProperties || super.hasProperties();
130 }
131
132 @Override
133 public boolean hasProperty(String relPath) throws RepositoryException {
134 boolean hasProperties = resolvedItems.entrySet().stream()
135 .anyMatch(entry -> !entry.getValue().isNode() && entry.getKey().equals(relPath));
136 return hasProperties || super.hasProperty(relPath);
137 }
138
139 @Override
140 public PropertyIterator getProperties() throws RepositoryException {
141 return getProperties("*");
142 }
143
144 @Override
145 public PropertyIterator getProperties(String namePattern) throws RepositoryException {
146 return getProperties(new String[] {namePattern});
147 }
148
149 @Override
150 public PropertyIterator getProperties(String[] nameGlobs) throws RepositoryException {
151 Iterator properties = Iterators.filterIterator(Iterators.properties(super.getProperties(nameGlobs)), property -> {
152 try {
153 return !resolvedItems.containsKey(property.getName());
154 } catch (RepositoryException e) {
155 return false;
156 }
157 });
158
159 List<Property> merged = Lists.newArrayList(properties);
160 List<Property> resolvedProperties = resolvedItems.entrySet().stream()
161 .filter(entry -> !entry.getValue().isNode() && ItemNameMatcher.matches(entry.getKey(), nameGlobs))
162 .map(entry -> (Property) entry.getValue())
163 .collect(Collectors.toList());
164 merged.addAll(resolvedProperties);
165
166 return new PropertyIteratorAdapter(merged);
167 }
168
169 @Override
170 public boolean hasNodes() throws RepositoryException {
171 boolean hasNodes = resolvedItems.values().stream()
172 .anyMatch(Item::isNode);
173 return hasNodes || super.hasNodes();
174 }
175
176 @Override
177 public boolean hasNode(String relPath) throws RepositoryException {
178 boolean hasNodes = resolvedItems.entrySet().stream()
179 .anyMatch(entry ->entry.getValue().isNode() && entry.getKey().equals(relPath) );
180 return hasNodes || super.hasNode(relPath);
181 }
182
183 @Override
184 public Node getNode(String relPath) throws RepositoryException {
185 Optional<Item> itemOptional = resolvedItems.entrySet().stream()
186 .filter(entry -> entry.getValue().isNode() && entry.getKey().equals(relPath))
187 .findFirst()
188 .map(Map.Entry::getValue);
189
190 if (itemOptional.isPresent()) {
191 return (Node) itemOptional.get();
192 }
193
194 return super.getNode(relPath);
195 }
196
197 @Override
198 public NodeIterator getNodes() throws RepositoryException {
199 return getNodes("*");
200 }
201
202 @Override
203 public NodeIterator getNodes(String namePattern) throws RepositoryException {
204 return getNodes(new String[] {namePattern});
205 }
206
207 @Override
208 public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
209 Collection<Node> children = NodeUtil.getCollectionFromNodeIterator(getWrappedNode().getNodes(nameGlobs));
210
211 List<Node> merged = new ArrayList<>(children);
212 List<Node> resolvedNodes = resolvedItems.entrySet().stream()
213 .filter(entry -> ItemNameMatcher.matches(entry.getKey(), nameGlobs) && entry.getValue().isNode())
214 .map(entry -> (Node) entry.getValue())
215 .collect(Collectors.toList());
216 merged.addAll(resolvedNodes);
217
218 return wrapNodeIterator(new NodeIteratorAdapter(merged));
219 }
220
221 @Override
222 protected NodeIterator wrapNodeIterator(NodeIterator nodeIterator) {
223 if (expandDecorators != null) {
224 return expandDecorators.wrapNodeIterator(nodeIterator);
225 }
226
227 return nodeIterator;
228 }
229
230 @Override
231 protected Node wrapNode(Node node) {
232 if (expandDecorators != null) {
233 return expandDecorators.wrapNode(node);
234 }
235
236 return node;
237 }
238 }