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