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.freemarker;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.context.WebContext;
39 import info.magnolia.i18nsystem.TranslationService;
40 import info.magnolia.objectfactory.Components;
41
42 import java.io.IOException;
43 import java.io.Reader;
44 import java.io.Writer;
45 import java.util.Locale;
46 import java.util.Map;
47 import java.util.Set;
48
49 import javax.inject.Inject;
50 import javax.inject.Singleton;
51 import javax.servlet.GenericServlet;
52 import javax.servlet.ServletContext;
53 import javax.servlet.ServletException;
54
55 import freemarker.cache.TemplateLoader;
56 import freemarker.ext.jsp.TaglibFactory;
57 import freemarker.ext.servlet.FreemarkerServlet;
58 import freemarker.ext.servlet.HttpRequestHashModel;
59 import freemarker.ext.servlet.ServletContextHashModel;
60 import freemarker.template.Configuration;
61 import freemarker.template.ObjectWrapper;
62 import freemarker.template.Template;
63 import freemarker.template.TemplateException;
64 import freemarker.template.TemplateModel;
65 import freemarker.template.TemplateModelException;
66
67
68
69
70
71
72 @Singleton
73 public class FreemarkerHelper {
74
75
76
77
78 @Deprecated
79 public static FreemarkerHelper getInstance() {
80 return Components.getSingleton(FreemarkerHelper.class);
81 }
82
83 private final Configuration cfg;
84 private final TranslationService translationService;
85
86
87 private TaglibFactory taglibFactory;
88 private ServletContextHashModel servletContextHashModel;
89
90 @Inject
91 public FreemarkerHelper(final FreemarkerConfig freemarkerConfig, TranslationService transactionService) {
92 this.translationService = transactionService;
93
94
95 this.cfg = new Configuration() {
96 @Override
97 public Set getSharedVariableNames() {
98 final Set names = super.getSharedVariableNames();
99 names.addAll(freemarkerConfig.getSharedVariables().keySet());
100 return names;
101 }
102
103 @Override
104 public TemplateModel getSharedVariable(String name) {
105 final TemplateModel value = super.getSharedVariable(name);
106 if (value == null) {
107 return freemarkerConfig.getSharedVariables().get(name);
108 }
109 return value;
110 }
111 };
112 cfg.setTemplateExceptionHandler(freemarkerConfig.getTemplateExceptionHandler());
113
114
115
116
117 cfg.setTemplateLoader(new ConfigDelegatingTemplateLoader(freemarkerConfig));
118 cfg.setObjectWrapper(new ConfigDelegatingObjectWrapper(freemarkerConfig));
119
120 cfg.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
121 cfg.setDefaultEncoding("UTF-8");
122 cfg.setURLEscapingCharset("UTF-8");
123
124 }
125
126
127
128
129 @Deprecated
130 public FreemarkerHelper(final FreemarkerConfig freemarkerConfig) {
131 this(freemarkerConfig, Components.getComponent(TranslationService.class));
132 }
133
134
135
136
137 public void render(String templatePath, Object root, Writer out) throws TemplateException, IOException {
138 render(templatePath, null, null, root, out);
139 }
140
141
142
143
144
145
146
147
148
149
150
151 public void render(String templatePath, Locale locale, String i18nBasename, Object root, Writer out) throws TemplateException, IOException {
152 final Locale localeToUse = checkLocale(locale);
153 prepareRendering(localeToUse, i18nBasename, root);
154
155 final Template template = cfg.getTemplate(templatePath, localeToUse);
156 template.process(root, out);
157 }
158
159
160
161
162
163
164
165 public void render(Reader template, Object root, Writer out) throws TemplateException, IOException {
166 render(template, null, null, root, out);
167 }
168
169 protected void render(Reader template, Locale locale, String i18nBasename, Object root, Writer out) throws TemplateException, IOException {
170 final Locale localeToUse = checkLocale(locale);
171 prepareRendering(localeToUse, i18nBasename, root);
172
173 final Template t = new Template("inlinetemplate", template, cfg);
174 t.setLocale(localeToUse);
175 t.process(root, out);
176 }
177
178
179
180
181 protected Locale checkLocale(Locale locale) {
182 if (locale != null) {
183 return locale;
184 } else if (MgnlContext.hasInstance()) {
185 return MgnlContext.getLocale();
186 } else {
187 return Locale.getDefault();
188 }
189 }
190
191
192
193
194 protected void prepareRendering(Locale checkedLocale, String i18nBasename, Object root) {
195 if (root instanceof Map) {
196 final Map<String, Object> data = (Map<String, Object>) root;
197 addDefaultData(data, checkedLocale, i18nBasename);
198 }
199 }
200
201 protected void addDefaultData(Map<String, Object> data, Locale locale, String i18nBasename) {
202 if (MgnlContext.hasInstance()) {
203 data.put("ctx", MgnlContext.getInstance());
204 }
205 if (MgnlContext.isWebContext()) {
206 final WebContext webCtx = MgnlContext.getWebContext();
207
208 data.put("contextPath", webCtx.getContextPath());
209 data.put("aggregationState", webCtx.getAggregationState());
210
211 addTaglibSupportData(data, webCtx);
212 }
213
214 data.put("defaultBaseUrl", ServerConfiguration.getInstance().getDefaultBaseUrl());
215
216 data.put("i18n", new MessagesWrapper(i18nBasename, locale, translationService));
217 data.put("i18nAuthoring", new MessagesWrapper(i18nBasename, MgnlContext.getLocale(), translationService));
218
219
220
221
222
223
224
225 }
226
227 protected void addTaglibSupportData(Map<String, Object> data, WebContext webCtx) {
228 final ServletContext servletContext = webCtx.getServletContext();
229 try {
230 data.put(FreemarkerServlet.KEY_JSP_TAGLIBS, checkTaglibFactory(servletContext));
231 data.put(FreemarkerServlet.KEY_APPLICATION_PRIVATE, checkServletContextModel(servletContext));
232 data.put(FreemarkerServlet.KEY_REQUEST_PRIVATE, new HttpRequestHashModel(webCtx.getRequest(), webCtx.getResponse(), cfg.getObjectWrapper()));
233 } catch (ServletException e) {
234
235 throw new RuntimeException("Can't initialize taglib support for FreeMarker: ", e);
236 }
237 }
238
239 protected TaglibFactory checkTaglibFactory(ServletContext servletContext) {
240 if (taglibFactory == null) {
241 taglibFactory = new TaglibFactory(new FreemarkerServletContextWrapper(servletContext));
242 }
243 return taglibFactory;
244 }
245
246 protected ServletContextHashModel checkServletContextModel(ServletContext servletContext) throws ServletException {
247 if (servletContextHashModel == null) {
248
249 final GenericServlet fs = new DoNothingServlet(servletContext);
250 servletContextHashModel = new ServletContextHashModel(fs, cfg.getObjectWrapper());
251 }
252 return servletContextHashModel;
253 }
254
255 protected Configuration getConfiguration() {
256 return cfg;
257 }
258
259 private static class ConfigDelegatingTemplateLoader implements TemplateLoader {
260 private final FreemarkerConfig freemarkerConfig;
261
262 public ConfigDelegatingTemplateLoader(FreemarkerConfig freemarkerConfig) {
263 this.freemarkerConfig = freemarkerConfig;
264 }
265
266 @Override
267 public Object findTemplateSource(String name) throws IOException {
268 return freemarkerConfig.getTemplateLoader().findTemplateSource(name);
269 }
270
271 @Override
272 public long getLastModified(Object templateSource) {
273 return freemarkerConfig.getTemplateLoader().getLastModified(templateSource);
274 }
275
276 @Override
277 public Reader getReader(Object templateSource, String encoding) throws IOException {
278 return freemarkerConfig.getTemplateLoader().getReader(templateSource, encoding);
279 }
280
281 @Override
282 public void closeTemplateSource(Object templateSource) throws IOException {
283 freemarkerConfig.getTemplateLoader().closeTemplateSource(templateSource);
284 }
285 }
286
287 private static class ConfigDelegatingObjectWrapper implements ObjectWrapper {
288 private final FreemarkerConfig freemarkerConfig;
289
290 public ConfigDelegatingObjectWrapper(FreemarkerConfig freemarkerConfig) {
291 this.freemarkerConfig = freemarkerConfig;
292 }
293
294 @Override
295 public TemplateModel wrap(Object obj) throws TemplateModelException {
296 return freemarkerConfig.getObjectWrapper().wrap(obj);
297 }
298 }
299 }