View Javadoc

1   /**
2    * This file Copyright (c) 2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.rendering.context;
35  
36  import info.magnolia.cms.core.AggregationState;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.util.ContentUtil;
39  import info.magnolia.objectfactory.annotation.RequestScoped;
40  import info.magnolia.rendering.engine.OutputProvider;
41  import info.magnolia.rendering.engine.RenderException;
42  import info.magnolia.rendering.engine.RenderExceptionHandler;
43  import info.magnolia.rendering.template.RenderableDefinition;
44  import info.magnolia.rendering.util.AppendableWriter;
45  
46  import java.io.IOException;
47  import java.io.OutputStream;
48  import java.io.Writer;
49  import java.util.EmptyStackException;
50  import java.util.Stack;
51  
52  import javax.inject.Inject;
53  import javax.inject.Provider;
54  import javax.jcr.Node;
55  
56  /**
57   * RenderingContext implementation that uses AggregationState.
58   *
59   * @version $Id$
60   */
61  @RequestScoped
62  public class AggregationStateBasedRenderingContext implements RenderingContext {
63  
64      // TODO dlipp: add reasonable javadoc! Uses and updates the {@link AggregationState}.
65      // FIXME we should not use the AggregationState anymore
66  
67      private static class StackState {
68  
69          RenderableDefinition renderableDefinition;
70          OutputProvider outputProvider;
71          Content legacyContent;
72  
73          private StackState(RenderableDefinition renderableDefinition, OutputProvider outputProvider, Content legacyContent) {
74              this.renderableDefinition = renderableDefinition;
75              this.outputProvider = outputProvider;
76              this.legacyContent = legacyContent;
77          }
78      }
79  
80      private final AggregationState aggregationState;
81      private final Stack<StackState> stack = new Stack<StackState>();
82      private RenderableDefinition currentRenderableDefinition;
83      private OutputProvider currentOutputProvider;
84      private RenderExceptionHandler exceptionHandler;
85  
86      /**
87       * We keep the current state in local variables and start using the stack only for the second push operation. This
88       * variable is 0 before the first push, 1 when we have local variables set, and greater than 1 when we have things
89       * on stack.
90       */
91      private int depth = 0;
92  
93      @Inject
94      public AggregationStateBasedRenderingContext(Provider<AggregationState> aggregationStateProvider, RenderExceptionHandler exceptionHandler) {
95          this(aggregationStateProvider.get(), exceptionHandler);
96      }
97  
98      public AggregationStateBasedRenderingContext(AggregationState aggregationState, RenderExceptionHandler exceptionHandler) {
99          this.aggregationState = aggregationState;
100         this.exceptionHandler = exceptionHandler;
101     }
102 
103     @Override
104     public Node getMainContent() {
105         // there is still a possibility of call to this method before push!
106         Content mainContent = aggregationState.getMainContent();
107         return mainContent != null ? mainContent.getJCRNode() : null;
108     }
109 
110     @Override
111     public Node getCurrentContent() {
112         Content currentContent = aggregationState.getCurrentContent();
113         return currentContent != null ? currentContent.getJCRNode() : null;
114     }
115 
116     @Override
117     public RenderableDefinition getRenderableDefinition() {
118         return currentRenderableDefinition;
119     }
120 
121     @Override
122     public void push(Node content, RenderableDefinition renderableDefinition) {
123         push(content, renderableDefinition, null);
124     }
125 
126     @Override
127     public void push(Node content, RenderableDefinition renderableDefinition, OutputProvider outputProvider) {
128 
129         // Creating the Content object can fail with an exception, by doing it before anything else we don't risk ending
130         // up having inconsistent state due to a partially completed push.
131         Content legacyContent = ContentUtil.asContent(content);
132 
133         if (aggregationState.getMainContent() == null) {
134             aggregationState.setMainContent(legacyContent);
135         }
136 
137         if (depth > 0) {
138             stack.push(new StackState(currentRenderableDefinition, currentOutputProvider, aggregationState.getCurrentContent()));
139         }
140         aggregationState.setCurrentContent(legacyContent);
141         currentRenderableDefinition = renderableDefinition;
142         currentOutputProvider = outputProvider != null ? outputProvider : currentOutputProvider;
143         depth++;
144     }
145 
146     @Override
147     public void pop() {
148         if (depth == 0) {
149             throw new EmptyStackException();
150         } else if (depth == 1) {
151             aggregationState.setCurrentContent(null);
152             currentRenderableDefinition = null;
153             currentOutputProvider = null;
154         } else {
155             StackState state = stack.pop();
156             aggregationState.setCurrentContent(state.legacyContent);
157             currentRenderableDefinition = state.renderableDefinition;
158             currentOutputProvider = state.outputProvider;
159         }
160         depth--;
161         // Note that we do not restore main content
162     }
163 
164     @Override
165     public OutputProvider getOutputProvider() {
166         return currentOutputProvider;
167     }
168 
169     @Override
170     public AppendableWriter getAppendable() throws RenderException, IOException {
171         return new AppendableWriter(this.currentOutputProvider.getAppendable());
172     }
173 
174     @Override
175     public OutputStream getOutputStream() throws RenderException, IOException {
176         return this.currentOutputProvider.getOutputStream();
177     }
178 
179     @Override
180     public void handleException(RenderException renderException, Writer out) {
181         exceptionHandler.handleException(renderException, out);
182     }
183 }