001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2006
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.common.web.view.freemarker;
025:
026: import java.io.IOException;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.Locale;
030: import java.util.Map;
031:
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034:
035: import org.riotfamily.common.web.view.MacroHelperFactory;
036: import org.springframework.web.servlet.support.RequestContextUtils;
037: import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
038:
039: import freemarker.ext.servlet.FreemarkerServlet;
040: import freemarker.template.Template;
041: import freemarker.template.TemplateException;
042:
043: /**
044: * Differences to Spring's FreeMarkerView:
045: * <ul>
046: * <li>Model attributes may override attributes from the request or the session</li>
047: * <li>The plain HttpServletRequest object is exposed under the key "request"</li>
048: * <li>The model is not exposed to the request, unless the
049: * {@link #setFreeMarkerServletMode(boolean) freeMarkeServletMode} is enabled</li>
050: * <li>Provides the possibility to create stateful macro helpers via the
051: * {@link MacroHelperFactory} interface</li>
052: * </ul>
053: */
054: public class RiotFreeMarkerView extends FreeMarkerView {
055:
056: public static final String REQUEST_KEY = "request";
057:
058: public static final Object TEMPLATE_NAME_KEY = RiotFreeMarkerView.class
059: .getName()
060: + ".templateName";
061:
062: public static final String MODEL_ATTRIBUTE = RiotFreeMarkerView.class
063: .getName()
064: + ".model";
065:
066: private boolean allowModelOverride = true;
067:
068: private boolean freeMarkerServletMode = false;
069:
070: private Map macroHelperFactories;
071:
072: /**
073: * Sets whether the model may contain keys that are also present as request
074: * or session attributes. Otherwise an exception will be thrown when Spring
075: * tries to expose an attribute that conflicts with a key in the model.
076: */
077: public void setAllowModelOverride(boolean allowModelOverride) {
078: this .allowModelOverride = allowModelOverride;
079: }
080:
081: /**
082: * Sets whether the view should mimic the behavior of the
083: * {@link FreemarkerServlet}, i.e. provide support JSP tag libraries, the
084: * "Request", "Session" and "Application" HashModels. By default this
085: * mode is disabled, mainly because JSP support requires exposure of the
086: * model as separate request attributes, which can be quite confusing when
087: * working with nested views.
088: *
089: * @param freeMarkerServletMode
090: */
091: public void setFreeMarkerServletMode(boolean freeMarkerServletMode) {
092: this .freeMarkerServletMode = freeMarkerServletMode;
093: }
094:
095: /**
096: * Sets a Map of {@link MacroHelperFactory} instances keyed by the
097: * variable name under which the created helper should be exposed.
098: */
099: public void setMacroHelperFactories(Map macroHelperFactories) {
100: this .macroHelperFactories = macroHelperFactories;
101: }
102:
103: public final void render(Map model, HttpServletRequest request,
104: HttpServletResponse response) throws Exception {
105:
106: if (allowModelOverride) {
107: Map emptyModel = new HashMap();
108: emptyModel.put(MODEL_ATTRIBUTE, model);
109: model = emptyModel;
110: }
111: super .render(model, request, wrapResponse(request, response));
112: }
113:
114: /**
115: * Subclasses may override this method in order to wrap the
116: * HttpServletResponse before it is passed on to the render method.
117: * The default implementation simply returns the given response.
118: */
119: protected HttpServletResponse wrapResponse(
120: HttpServletRequest request, HttpServletResponse response) {
121:
122: return response;
123: }
124:
125: private void unwrapModel(Map model) {
126: Map originalModel = (Map) model.remove(MODEL_ATTRIBUTE);
127: if (originalModel != null) {
128: model.putAll(originalModel);
129: }
130: }
131:
132: protected void renderMergedTemplateModel(final Map model,
133: final HttpServletRequest request,
134: final HttpServletResponse response) throws Exception {
135:
136: unwrapModel(model);
137: model.put(REQUEST_KEY, request);
138: if (macroHelperFactories != null) {
139: Iterator it = macroHelperFactories.entrySet().iterator();
140: while (it.hasNext()) {
141: Map.Entry entry = (Map.Entry) it.next();
142: MacroHelperFactory factory = (MacroHelperFactory) entry
143: .getValue();
144: model.put(entry.getKey(), factory.createMacroHelper(
145: request, response));
146: }
147: }
148: if (freeMarkerServletMode) {
149: super .doRender(model, request, response);
150: } else {
151: if (logger.isDebugEnabled()) {
152: logger.debug("Rendering FreeMarker template ["
153: + getUrl() + "] in FreeMarkerView '"
154: + getBeanName() + "'");
155: }
156: Locale locale = RequestContextUtils.getLocale(request);
157: processTemplate(getTemplate(locale), model, response);
158: }
159: }
160:
161: protected void processTemplate(Template template, Map model,
162: HttpServletResponse response) throws IOException,
163: TemplateException {
164:
165: model.put(TEMPLATE_NAME_KEY, template.getName());
166: super.processTemplate(template, model, response);
167: }
168:
169: }
|