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.LocalScoped;
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.util.EmptyStackException;
49  import java.util.Stack;
50  
51  import javax.inject.Inject;
52  import javax.inject.Provider;
53  import javax.jcr.Node;
54  
55  /**
56   * RenderingContext implementation that uses AggregationState.
57   *
58   * @version $Id$
59   */
60  @LocalScoped
61  public class AggregationStateBasedRenderingContext implements RenderingContext {
62  
63      // TODO dlipp: add reasonable javadoc! Uses and updates the {@link AggregationState}.
64      // FIXME we should not use the AggregationState anymore
65  
66      private static class StackState {
67  
68          RenderableDefinition renderableDefinition;
69          OutputProvider outputProvider;
70          Content legacyContent;
71  
72          private StackState(RenderableDefinition renderableDefinition, OutputProvider outputProvider, Content legacyContent) {
73              this.renderableDefinition = renderableDefinition;
74              this.outputProvider = outputProvider;
75              this.legacyContent = legacyContent;
76          }
77      }
78  
79      private final AggregationState aggregationState;
80      private final Stack<StackState> stack = new Stack<StackState>();
81      private RenderableDefinition currentRenderableDefinition;
82      private OutputProvider currentOutputProvider;
83      private RenderExceptionHandler exceptionHandler;
84  
85      /**
86       * We keep the current state in local variables and start using the stack only for the second push operation. This
87       * variable is 0 before the first push, 1 when we have local variables set, and greater than 1 when we have things
88       * on stack.
89       */
90      private int depth = 0;
91  
92      @Inject
93      public AggregationStateBasedRenderingContext(Provider<AggregationState> aggregationStateProvider, RenderExceptionHandler exceptionHandler) {
94          this(aggregationStateProvider.get(), exceptionHandler);
95      }
96  
97      public AggregationStateBasedRenderingContext(AggregationState aggregationState, RenderExceptionHandler exceptionHandler) {
98          this.aggregationState = aggregationState;
99          this.exceptionHandler = exceptionHandler;
100     }
101 
102     @Override
103     public Node getMainContent() {
104         // there is still a possibility of call to this method before push!
105         Content mainContent = aggregationState.getMainContent();
106         return mainContent != null ? mainContent.getJCRNode() : null;
107     }
108 
109     @Override
110     public Node getCurrentContent() {
111         Content currentContent = aggregationState.getCurrentContent();
112         return currentContent != null ? currentContent.getJCRNode() : null;
113     }
114 
115     @Override
116     public RenderableDefinition getRenderableDefinition() {
117         return currentRenderableDefinition;
118     }
119 
120     @Override
121     public void push(Node content, RenderableDefinition renderableDefinition) {
122         push(content, renderableDefinition, null);
123     }
124 
125     @Override
126     public void push(Node content, RenderableDefinition renderableDefinition, OutputProvider outputProvider) {
127 
128         // Creating the Content object can fail with an exception, by doing it before anything else we don't risk ending
129         // up having inconsistent state due to a partially completed push.
130         Content legacyContent = ContentUtil.asContent(content);
131 
132         if (aggregationState.getMainContent() == null) {
133             aggregationState.setMainContent(legacyContent);
134         }
135 
136         if (depth > 0) {
137             stack.push(new StackState(currentRenderableDefinition, currentOutputProvider, aggregationState.getCurrentContent()));
138         }
139         aggregationState.setCurrentContent(legacyContent);
140         currentRenderableDefinition = renderableDefinition;
141         currentOutputProvider = outputProvider != null ? outputProvider : currentOutputProvider;
142         depth++;
143     }
144 
145     @Override
146     public void pop() {
147         if (depth == 0) {
148             throw new EmptyStackException();
149         } else if (depth == 1) {
150             aggregationState.setCurrentContent(null);
151             currentRenderableDefinition = null;
152             currentOutputProvider = null;
153         } else {
154             StackState state = stack.pop();
155             aggregationState.setCurrentContent(state.legacyContent);
156             currentRenderableDefinition = state.renderableDefinition;
157             currentOutputProvider = state.outputProvider;
158         }
159         depth--;
160         // Note that we do not restore main content
161     }
162 
163     @Override
164     public OutputProvider getOutputProvider() {
165         return currentOutputProvider;
166     }
167 
168     @Override
169     public AppendableWriter getAppendable() throws IOException {
170         return new AppendableWriter(this.currentOutputProvider.getAppendable());
171     }
172 
173     @Override
174     public OutputStream getOutputStream() throws IOException {
175         return this.currentOutputProvider.getOutputStream();
176     }
177 
178     @Override
179     public void handleException(RenderException renderException) {
180         exceptionHandler.handleException(renderException, this);
181     }
182 }