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