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.TemplateScalarModel;
46 import freemarker.template.TemplateSequenceModel;
47 import freemarker.template.utility.DeepUnwrap;
48 import info.magnolia.cms.core.Content;
49 import info.magnolia.freemarker.models.ContentMapModel;
50 import info.magnolia.freemarker.models.ContentModel;
51 import info.magnolia.objectfactory.Components;
52 import info.magnolia.rendering.context.RenderingContext;
53 import info.magnolia.rendering.engine.RenderException;
54 import info.magnolia.rendering.engine.RenderingEngine;
55 import info.magnolia.templating.elements.AbstractContentTemplatingElement;
56 import info.magnolia.templating.elements.TemplatingElement;
57
58 import java.io.IOException;
59 import java.lang.reflect.ParameterizedType;
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.Map;
64
65 import javax.jcr.Node;
66
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 @Deprecated
174 protected Content mandatoryContent(Map<String, TemplateModel> params, String key) throws TemplateModelException {
175 return _param(params, key, ContentModel.class, true).asContent();
176 }
177
178 @Deprecated
179 protected Content content(Map<String, TemplateModel> params, String key, Content defaultValue) throws TemplateModelException {
180 final ContentModel m = _param(params, key, ContentModel.class, false);
181 if (m == null) {
182 return defaultValue;
183 }
184 return m.asContent();
185 }
186
187 protected Node node(Map<String, TemplateModel> params, String key, Node defaultValue) throws TemplateModelException {
188 final ContentMapModel m = _param(params, key, ContentMapModel.class, false);
189 if (m == null) {
190 return defaultValue;
191 }
192 return m.getJCRNode();
193 }
194
195 protected Object object(Map<String, TemplateModel> params, String key) throws TemplateModelException {
196 final TemplateModel model = _param(params, key, TemplateModel.class, false);
197 if (model == null) {
198 return null;
199 }
200 return DeepUnwrap.unwrap(model);
201 }
202
203 protected Object mandatoryObject(Map<String, TemplateModel> params, String key) throws TemplateModelException {
204 final TemplateModel model = _param(params, key, TemplateModel.class, true);
205 return DeepUnwrap.unwrap(model);
206 }
207
208 protected List<String> mandatoryStringList(Map<String, TemplateModel> params, String key) throws TemplateModelException {
209 final TemplateModel model = _param(params, key, TemplateModel.class, true);
210 if (model instanceof TemplateScalarModel) {
211 final String s = ((TemplateScalarModel) model).getAsString();
212 return Collections.singletonList(s);
213 } else if (model instanceof TemplateSequenceModel) {
214 final CollectionAndSequence coll = new CollectionAndSequence((TemplateSequenceModel) model);
215 return unwrapStringList(coll, key);
216 } else if (model instanceof TemplateCollectionModel) {
217 final CollectionAndSequence coll = new CollectionAndSequence((TemplateCollectionModel) model);
218 return unwrapStringList(coll, key);
219 } else {
220 throw new TemplateModelException(key + " must be a String, a Collection of Strings. Found " + model.getClass().getSimpleName() + ".");
221 }
222 }
223
224 private List<String> unwrapStringList(CollectionAndSequence collAndSeq, String key) throws TemplateModelException {
225 final List<String> list = new ArrayList<String>();
226 for (int i = 0; i < collAndSeq.size(); i++) {
227 final TemplateModel tm = collAndSeq.get(i);
228 if (tm instanceof TemplateScalarModel) {
229 list.add(((TemplateScalarModel) tm).getAsString());
230 } else {
231 throw new TemplateModelException("The '" + key + "' attribute must be a String or a Collection of Strings. Found Collection of " + tm.getClass().getSimpleName() + ".");
232 }
233 }
234 return list;
235 }
236
237 protected <MT extends TemplateModel> MT _param(Map<String, TemplateModel> params, String key, Class<MT> type, boolean isMandatory) throws TemplateModelException {
238 final boolean containsKey = params.containsKey(key);
239 if (isMandatory && !containsKey) {
240 throw new TemplateModelException("The '" + key + "' parameter is mandatory.");
241
242 }
243
244 final TemplateModel m = params.get(key);
245 if (m != null && !type.isAssignableFrom(m.getClass())) {
246 throw new TemplateModelException("The '" + key + "' parameter must be a " + type.getSimpleName() + " and is a " + m.getClass().getSimpleName() + ".");
247 }
248 if (m == null && containsKey) {
249
250 throw new TemplateModelException("The '" + key + "' parameter was passed but not or wrongly specified.");
251 }
252 if (containsKey) {
253 params.remove(key);
254 }
255
256 return (MT) m;
257 }
258
259
260
261
262 protected void initContentElement(Map<String, TemplateModel> params, AbstractContentTemplatingElement component) throws TemplateModelException {
263 Node content = node(params, CONTENT_ATTRIBUTE, null);
264 String workspace = string(params, WORKSPACE_ATTRIBUTE, null);
265 String nodeIdentifier = string(params, UUID_ATTRIBUTE, null);
266 String path = string(params, PATH_ATTRIBUTE, null);
267
268 component.setContent(content);
269 component.setWorkspace(workspace);
270 component.setNodeIdentifier(nodeIdentifier);
271 component.setPath(path);
272 }
273 }