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