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.rendering.context;
35
36 import info.magnolia.cms.core.AggregationState;
37 import info.magnolia.objectfactory.annotation.LocalScoped;
38 import info.magnolia.rendering.engine.OutputProvider;
39 import info.magnolia.rendering.engine.RenderException;
40 import info.magnolia.rendering.engine.RenderExceptionHandler;
41 import info.magnolia.rendering.listeners.AbstractRenderingListener;
42 import info.magnolia.rendering.listeners.AbstractRenderingListener.RenderingListenerReturnCode;
43 import info.magnolia.rendering.template.AreaDefinition;
44 import info.magnolia.rendering.template.RenderableDefinition;
45 import info.magnolia.rendering.util.AppendableWriter;
46
47 import java.io.IOException;
48 import java.io.OutputStream;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.EmptyStackException;
52 import java.util.Enumeration;
53 import java.util.Iterator;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.ListIterator;
57 import java.util.Map;
58 import java.util.Stack;
59
60 import javax.inject.Inject;
61 import javax.inject.Provider;
62 import javax.jcr.Node;
63
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67
68
69
70 @LocalScoped
71 public class AggregationStateBasedRenderingContext implements RenderingContext {
72
73
74
75 private final Logger log = LoggerFactory.getLogger(getClass());
76
77 private static class StackState {
78
79 RenderableDefinition renderableDefinition;
80 OutputProvider outputProvider;
81 Node legacyContent;
82
83 private StackState(RenderableDefinition renderableDefinition, OutputProvider outputProvider, Node legacyContent) {
84 this.renderableDefinition = renderableDefinition;
85 this.outputProvider = outputProvider;
86 this.legacyContent = legacyContent;
87 }
88 }
89
90 private final AggregationState aggregationState;
91 private final Stack<StackState> stack = new Stack<StackState>();
92 private RenderableDefinition currentRenderableDefinition;
93 private OutputProvider currentOutputProvider;
94 private final RenderExceptionHandler exceptionHandler;
95 private List<AbstractRenderingListener> listeners = new LinkedList<AbstractRenderingListener>();
96
97
98
99
100
101
102 private int depth = 0;
103
104 @Inject
105 public AggregationStateBasedRenderingContext(Provider<AggregationState> aggregationStateProvider, RenderExceptionHandler exceptionHandler) {
106 this(aggregationStateProvider.get(), exceptionHandler);
107 }
108
109 public AggregationStateBasedRenderingContext(AggregationState aggregationState, RenderExceptionHandler exceptionHandler) {
110 this.aggregationState = aggregationState;
111 this.exceptionHandler = exceptionHandler;
112 }
113
114 @Override
115 public Node getMainContent() {
116
117 return aggregationState.getMainContentNode();
118 }
119
120 @Override
121 public Node getCurrentContent() {
122 return aggregationState.getCurrentContentNode();
123 }
124
125 @Override
126 public RenderableDefinition getRenderableDefinition() {
127 return currentRenderableDefinition;
128 }
129
130 @Override
131 public AreaDefinition getParentAreaDefinition() {
132 if (currentRenderableDefinition instanceof AreaDefinition) {
133 return (AreaDefinition) currentRenderableDefinition;
134 }
135
136 Enumeration<StackState> elements = stack.elements();
137 while (elements.hasMoreElements()) {
138 StackState state = elements.nextElement();
139 if (state.renderableDefinition instanceof AreaDefinition) {
140 return (AreaDefinition) state.renderableDefinition;
141 }
142 }
143 return null;
144 }
145
146
147
148
149
150 @Override
151 public RenderableDefinition getAncestorDefinition(int level) {
152 if (level <= 0 || level > stack.size()) {
153 log.error("Level {} ancestor of renderable definition for node '{}' doesn't exist.", level, this.getCurrentContent());
154 return null;
155 }
156 int index = stack.size() - level;
157 return stack.get(index).renderableDefinition;
158 }
159
160 @Override
161 public void push(Node content, RenderableDefinition renderableDefinition) {
162 push(content, renderableDefinition, null);
163 }
164
165 @Override
166 public void push(Node content, RenderableDefinition renderableDefinition, OutputProvider outputProvider) {
167
168
169
170 Node legacyContent = content;
171
172 if (aggregationState.getMainContentNode() == null) {
173 aggregationState.setMainContentNode(content);
174 }
175
176 if (depth > 0) {
177 stack.push(new StackState(currentRenderableDefinition, currentOutputProvider, aggregationState.getCurrentContentNode()));
178 }
179 aggregationState.setCurrentContentNode(content);
180 currentRenderableDefinition = renderableDefinition;
181 currentOutputProvider = outputProvider != null ? outputProvider : currentOutputProvider;
182 depth++;
183 }
184
185 @Override
186 public void pop() {
187 if (depth == 0) {
188 throw new EmptyStackException();
189 } else if (depth == 1) {
190 aggregationState.setCurrentContentNode(null);
191 currentRenderableDefinition = null;
192 currentOutputProvider = null;
193 } else {
194 StackState state = stack.pop();
195 aggregationState.setCurrentContentNode(state.legacyContent);
196 currentRenderableDefinition = state.renderableDefinition;
197 currentOutputProvider = state.outputProvider;
198 }
199 depth--;
200
201 }
202
203 @Override
204 public OutputProvider getOutputProvider() {
205 return currentOutputProvider;
206 }
207
208 @Override
209 public AppendableWriter getAppendable() throws IOException {
210 return new AppendableWriter(this.currentOutputProvider.getAppendable());
211 }
212
213 @Override
214 public OutputStream getOutputStream() throws IOException {
215 return this.currentOutputProvider.getOutputStream();
216 }
217
218 @Override
219 public void handleException(RenderException renderException) {
220 exceptionHandler.handleException(renderException, this);
221 }
222
223 @Override
224 public void setListeners(List<AbstractRenderingListener> listeners) {
225 this.listeners = listeners;
226 }
227
228 @Override
229 public void addListener(AbstractRenderingListener renderingListener) {
230 this.listeners.add(renderingListener);
231 }
232
233
234 @Override
235 public Collection<RenderingListenerReturnCode> before(Node content, RenderableDefinition definition, Map<String, Object> contextObjects, OutputProvider out) {
236 Iterator<AbstractRenderingListener> iterator = this.listeners.iterator();
237 Collection<RenderingListenerReturnCode> results = new ArrayList<RenderingListenerReturnCode>();
238
239 while (iterator.hasNext()) {
240 AbstractRenderingListener listener = iterator.next();
241 try {
242 RenderingListenerReturnCode result = listener.before(content, definition, contextObjects, out);
243 if (result != null) {
244 results.add(result);
245 }
246 } catch (Exception e) {
247 log.error("{}.before() failed with exception ", listener, e);
248 }
249 }
250 return results;
251 }
252
253
254 @Override
255 public void after(Node content, RenderableDefinition definition, Map<String, Object> contextObjects, OutputProvider out) {
256 ListIterator<AbstractRenderingListener> iterator = this.listeners.listIterator(listeners.size());
257 Collection<RenderingListenerReturnCode> results = new ArrayList<RenderingListenerReturnCode>();
258
259 while (iterator.hasPrevious()) {
260 AbstractRenderingListener listener = iterator.previous();
261 try {
262 RenderingListenerReturnCode result = listener.after(content, definition, contextObjects, out);
263 if (result != null) {
264 results.add(result);
265 }
266 } catch (Exception e) {
267 log.error("{}.after() failed with exception ", listener, e);
268 }
269 }
270 }
271 }