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.templating.freemarker;
35
36 import freemarker.core.CollectionAndSequence;
37 import freemarker.core.Environment;
38 import freemarker.template.TemplateBooleanModel;
39 import freemarker.template.TemplateCollectionModel;
40 import freemarker.template.TemplateDirectiveBody;
41 import freemarker.template.TemplateDirectiveModel;
42 import freemarker.template.TemplateException;
43 import freemarker.template.TemplateModel;
44 import freemarker.template.TemplateModelException;
45 import freemarker.template.TemplateNumberModel;
46 import freemarker.template.TemplateScalarModel;
47 import freemarker.template.TemplateSequenceModel;
48 import freemarker.template.utility.DeepUnwrap;
49
50 import info.magnolia.cms.core.Content;
51 import info.magnolia.freemarker.models.ContentMapModel;
52 import info.magnolia.freemarker.models.ContentModel;
53 import info.magnolia.objectfactory.Components;
54 import info.magnolia.rendering.context.RenderingContext;
55 import info.magnolia.rendering.engine.RenderException;
56 import info.magnolia.rendering.engine.RenderingEngine;
57 import info.magnolia.templating.elements.AbstractContentTemplatingElement;
58 import info.magnolia.templating.elements.TemplatingElement;
59
60 import java.io.IOException;
61 import java.lang.reflect.ParameterizedType;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.List;
65 import java.util.Map;
66
67 import javax.jcr.Node;
68
69
70
71
72
73
74 public abstract class AbstractDirective<C extends TemplatingElement> implements TemplateDirectiveModel {
75
76 public static final String PATH_ATTRIBUTE = "path";
77 public static final String UUID_ATTRIBUTE = "uuid";
78 public static final String WORKSPACE_ATTRIBUTE = "workspace";
79 public static final String CONTENT_ATTRIBUTE = "content";
80
81 @Override
82 public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
83
84 final C templatingElement = createTemplatingElement();
85
86 prepareTemplatingElement(templatingElement, env, params, loopVars, body);
87
88
89 if (!params.isEmpty()) {
90 throw new TemplateModelException("Unsupported parameter(s): " + params);
91 }
92
93 try {
94 templatingElement.begin(env.getOut());
95
96 try {
97 doBody(env, body);
98 } finally {
99 templatingElement.end(env.getOut());
100 }
101 } catch (RenderException e) {
102 throw new TemplateException(e, env);
103 }
104 }
105
106 protected C createTemplatingElement() {
107
108
109 final RenderingEngine renderingEngine = Components.getComponent(RenderingEngine.class);
110 final RenderingContext renderingContext = renderingEngine.getRenderingContext();
111
112 return Components.getComponentProvider().newInstance(getTemplatingElementClass(), renderingContext);
113 }
114
115 protected Class<C> getTemplatingElementClass() {
116
117 return (Class<C>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132 protected abstract void prepareTemplatingElement(C templatingElement, Environment env, Map<String, TemplateModel> params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateModelException, IOException;
133
134 protected void doBody(Environment env, TemplateDirectiveBody body) throws TemplateException, IOException {
135 if (body != null) {
136 body.render(env.getOut());
137 }
138 }
139
140
141
142
143
144 protected void checkBody(TemplateDirectiveBody body, boolean needsBody) throws TemplateModelException {
145 if ((body == null) == needsBody) {
146 throw new TemplateModelException("This directive " + (needsBody ? "needs a body" : "does not support a body"));
147 }
148 }
149
150 protected String mandatoryString(Map<String, TemplateModel> params, String key) throws TemplateModelException {
151 return _param(params, key, TemplateScalarModel.class, true).getAsString();
152 }
153
154 protected String string(Map<String, TemplateModel> params, String key, String defaultValue) throws TemplateModelException {
155 final TemplateScalarModel m = _param(params, key, TemplateScalarModel.class, false);
156 if (m == null) {
157 return defaultValue;
158 }
159 return m.getAsString();
160 }
161
162 protected boolean mandatoryBool(Map<String, TemplateModel> params, String key) throws TemplateModelException {
163 return _param(params, key, TemplateBooleanModel.class, true).getAsBoolean();
164 }
165
166 protected Boolean bool(Map<String, TemplateModel> params, String key, Boolean defaultValue) throws TemplateModelException {
167 final TemplateBooleanModel m = _param(params, key, TemplateBooleanModel.class, false);
168 if (m == null) {
169 return defaultValue;
170 }
171 return m.getAsBoolean();
172 }
173
174 protected Integer integer(Map<String, TemplateModel> params, String key, Integer defaultValue) throws TemplateModelException {
175 final TemplateNumberModel m = _param(params, key, TemplateNumberModel.class, false);
176 if (m == null) {
177 return defaultValue;
178 }
179 return m.getAsNumber().intValue();
180 }
181
182 @Deprecated
183 protected Content mandatoryContent(Map<String, TemplateModel> params, String key) throws TemplateModelException {
184 return _param(params, key, ContentModel.class, true).asContent();
185 }
186
187 @Deprecated
188 protected Content content(Map<String, TemplateModel> params, String key, Content defaultValue) throws TemplateModelException {
189 final ContentModel m = _param(params, key, ContentModel.class, false);
190 if (m == null) {
191 return defaultValue;
192 }
193 return m.asContent();
194 }
195
196 protected Node node(Map<String, TemplateModel> params, String key, Node defaultValue) throws TemplateModelException {
197 final ContentMapModel m = _param(params, key, ContentMapModel.class, false);
198 if (m == null) {
199 return defaultValue;
200 }
201 return m.getJCRNode();
202 }
203
204 protected Object object(Map<String, TemplateModel> params, String key) throws TemplateModelException {
205 final TemplateModel model = _param(params, key, TemplateModel.class, false);
206 if (model == null) {
207 return null;
208 }
209 return DeepUnwrap.unwrap(model);
210 }
211
212 protected Object mandatoryObject(Map<String, TemplateModel> params, String key) throws TemplateModelException {
213 final TemplateModel model = _param(params, key, TemplateModel.class, true);
214 return DeepUnwrap.unwrap(model);
215 }
216
217 protected List<String> mandatoryStringList(Map<String, TemplateModel> params, String key) throws TemplateModelException {
218 final TemplateModel model = _param(params, key, TemplateModel.class, true);
219 if (model instanceof TemplateScalarModel) {
220 final String s = ((TemplateScalarModel) model).getAsString();
221 return Collections.singletonList(s);
222 } else if (model instanceof TemplateSequenceModel) {
223 final CollectionAndSequence coll = new CollectionAndSequence((TemplateSequenceModel) model);
224 return unwrapStringList(coll, key);
225 } else if (model instanceof TemplateCollectionModel) {
226 final CollectionAndSequence coll = new CollectionAndSequence((TemplateCollectionModel) model);
227 return unwrapStringList(coll, key);
228 } else {
229 throw new TemplateModelException(key + " must be a String, a Collection of Strings. Found " + model.getClass().getSimpleName() + ".");
230 }
231 }
232
233 private List<String> unwrapStringList(CollectionAndSequence collAndSeq, String key) throws TemplateModelException {
234 final List<String> list = new ArrayList<String>();
235 for (int i = 0; i < collAndSeq.size(); i++) {
236 final TemplateModel tm = collAndSeq.get(i);
237 if (tm instanceof TemplateScalarModel) {
238 list.add(((TemplateScalarModel) tm).getAsString());
239 } else {
240 throw new TemplateModelException("The '" + key + "' attribute must be a String or a Collection of Strings. Found Collection of " + tm.getClass().getSimpleName() + ".");
241 }
242 }
243 return list;
244 }
245
246 protected <MT extends TemplateModel> MT _param(Map<String, TemplateModel> params, String key, Class<MT> type, boolean isMandatory) throws TemplateModelException {
247 final boolean containsKey = params.containsKey(key);
248 if (isMandatory && !containsKey) {
249 throw new TemplateModelException("The '" + key + "' parameter is mandatory.");
250
251 }
252
253 final TemplateModel m = params.get(key);
254 if (m != null && !type.isAssignableFrom(m.getClass())) {
255 throw new TemplateModelException("The '" + key + "' parameter must be a " + type.getSimpleName() + " and is a " + m.getClass().getSimpleName() + ".");
256 }
257 if (m == null && containsKey) {
258
259 throw new TemplateModelException("The '" + key + "' parameter was passed but not or wrongly specified.");
260 }
261 if (containsKey) {
262 params.remove(key);
263 }
264
265 return (MT) m;
266 }
267
268
269
270
271 protected void initContentElement(Map<String, TemplateModel> params, AbstractContentTemplatingElement component) throws TemplateModelException {
272 Node content = node(params, CONTENT_ATTRIBUTE, null);
273 String workspace = string(params, WORKSPACE_ATTRIBUTE, null);
274 String nodeIdentifier = string(params, UUID_ATTRIBUTE, null);
275 String path = string(params, PATH_ATTRIBUTE, null);
276
277 component.setContent(content);
278 component.setWorkspace(workspace);
279 component.setNodeIdentifier(nodeIdentifier);
280 component.setPath(path);
281 }
282 }