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.module.blossom.template;
35
36 import java.lang.reflect.Method;
37 import java.util.ArrayList;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import javax.jcr.Node;
42
43 import org.apache.commons.lang.StringUtils;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.util.ClassUtils;
47
48 import info.magnolia.cms.core.Content;
49 import info.magnolia.cms.util.ContentUtil;
50 import info.magnolia.module.blossom.annotation.Area;
51 import info.magnolia.module.blossom.annotation.Available;
52 import info.magnolia.module.blossom.annotation.AvailableComponentClasses;
53 import info.magnolia.module.blossom.annotation.AvailableComponents;
54 import info.magnolia.module.blossom.annotation.I18nBasename;
55 import info.magnolia.module.blossom.annotation.Inherits;
56 import info.magnolia.module.blossom.annotation.Template;
57 import info.magnolia.module.blossom.annotation.TemplateDescription;
58 import info.magnolia.module.blossom.annotation.TernaryBoolean;
59 import info.magnolia.module.blossom.dispatcher.BlossomDispatcher;
60 import info.magnolia.module.blossom.support.MethodInvocationUtils;
61 import info.magnolia.module.blossom.support.ParameterResolver;
62 import info.magnolia.rendering.template.AreaDefinition;
63 import info.magnolia.rendering.template.ComponentAvailability;
64 import info.magnolia.rendering.template.InheritanceConfiguration;
65 import info.magnolia.rendering.template.TemplateAvailability;
66 import info.magnolia.rendering.template.TemplateDefinition;
67 import info.magnolia.rendering.template.configured.ConfiguredComponentAvailability;
68 import info.magnolia.rendering.template.configured.ConfiguredInheritance;
69
70
71
72
73
74
75 public class TemplateDefinitionBuilder {
76
77 private final Logger logger = LoggerFactory.getLogger(getClass());
78
79 public BlossomTemplateDefinition buildTemplateDefinition(BlossomDispatcher dispatcher, DetectedHandlersMetaData detectedHandlers, HandlerMetaData template) {
80
81 Class<?> handlerClass = template.getHandlerClass();
82 Object handler = template.getHandler();
83 String handlerPath = template.getHandlerPath();
84 Template annotation = handlerClass.getAnnotation(Template.class);
85
86 BlossomTemplateDefinition definition = new BlossomTemplateDefinition();
87 definition.setId(resolveTemplateId(handlerClass));
88 definition.setName(definition.getId());
89 definition.setTitle(resolveTemplateTitle(template));
90 definition.setDescription(resolveDescription(template));
91 definition.setI18nBasename(getI18nBasename(template));
92 definition.setHandlerPath(handlerPath);
93 definition.setDialog(StringUtils.trimToNull(annotation.dialog()));
94 definition.setVisible(annotation.visible());
95 definition.setDispatcher(dispatcher);
96 definition.setHandler(handler);
97 TemplateAvailability templateAvailability = resolveTemplateAvailability(template);
98 if (templateAvailability != null) {
99 definition.setTemplateAvailability(templateAvailability);
100 }
101 definition.setRenderType("blossom");
102
103 definition.setAreas(buildAreaDefinitionsForTemplate(dispatcher, detectedHandlers, template));
104
105 return definition;
106 }
107
108 protected String resolveTemplateId(Class<?> handlerClass) {
109 Template annotation = handlerClass.getAnnotation(Template.class);
110 if (annotation == null) {
111 throw new IllegalStateException("");
112 }
113 return annotation.id();
114 }
115
116 protected String resolveTemplateTitle(HandlerMetaData template) {
117 Template annotation = template.getHandlerClass().getAnnotation(Template.class);
118 return annotation.title();
119 }
120
121 protected String resolveDescription(HandlerMetaData template) {
122 TemplateDescription templateDescription = template.getHandlerClass().getAnnotation(TemplateDescription.class);
123 if (templateDescription != null && StringUtils.isNotEmpty(templateDescription.value())) {
124 return templateDescription.value();
125 }
126 return "";
127 }
128
129 protected String getI18nBasename(HandlerMetaData handler) {
130 I18nBasename i18nBasename = handler.getHandlerClass().getAnnotation(I18nBasename.class);
131 return i18nBasename != null ? i18nBasename.value() : null;
132 }
133
134 protected Map<String, AreaDefinition> buildAreaDefinitionsForTemplate(BlossomDispatcher dispatcher, DetectedHandlersMetaData detectedHandlers, HandlerMetaData template) {
135 Map<String, AreaDefinition> areas = new HashMap<String, AreaDefinition>();
136 List<HandlerMetaData> list = detectedHandlers.getAreasByEnclosingClass(template.getHandlerClass());
137 if (list != null) {
138 for (HandlerMetaData area : list) {
139 BlossomAreaDefinition areaDefinition = buildAreaDefinition(dispatcher, detectedHandlers, area);
140 areas.put(areaDefinition.getId(), areaDefinition);
141 }
142 }
143 return areas;
144 }
145
146 protected BlossomAreaDefinition buildAreaDefinition(BlossomDispatcher dispatcher, DetectedHandlersMetaData detectedHandlers, HandlerMetaData area) {
147
148 Area annotation = area.getHandlerClass().getAnnotation(Area.class);
149
150 BlossomAreaDefinition definition = new BlossomAreaDefinition();
151 definition.setId(annotation.value());
152 definition.setName(annotation.value());
153 definition.setTitle(annotation.title());
154 definition.setRenderType("blossom");
155 definition.setHandlerPath(area.getHandlerPath());
156 definition.setHandler(area.getHandler());
157 definition.setDispatcher(dispatcher);
158 definition.setDialog(StringUtils.trimToNull(annotation.dialog()));
159 definition.setI18nBasename(getI18nBasename(area));
160 definition.setType(annotation.type().getDefinitionFormat());
161 if (annotation.optional() != TernaryBoolean.UNSPECIFIED) {
162 definition.setOptional(TernaryBoolean.toBoolean(annotation.optional()));
163 }
164
165
166
167
168 definition.setTemplateScript("<area-script-placeholder>");
169
170 definition.setInheritance(resolveInheritanceConfiguration(area));
171 definition.setAvailableComponents(resolveAvailableComponents(area));
172 definition.setAreas(buildAreaDefinitionsForTemplate(dispatcher, detectedHandlers, area));
173
174 return definition;
175 }
176
177 protected Map<String, ComponentAvailability> resolveAvailableComponents(HandlerMetaData area) {
178 Map<String, ComponentAvailability> map = new HashMap<String, ComponentAvailability>();
179 AvailableComponents availableComponents = area.getHandlerClass().getAnnotation(AvailableComponents.class);
180 if (availableComponents != null) {
181 for (String componentId : availableComponents.value()) {
182 ConfiguredComponentAvailability availability = new ConfiguredComponentAvailability();
183 availability.setId(componentId);
184 map.put(componentId, availability);
185 }
186 }
187 AvailableComponentClasses availableComponentClasses = area.getHandlerClass().getAnnotation(AvailableComponentClasses.class);
188 if (availableComponentClasses != null) {
189 for (Class<?> clazz : availableComponentClasses.value()) {
190 ConfiguredComponentAvailability availability = new ConfiguredComponentAvailability();
191 String componentId = resolveTemplateId(clazz);
192 availability.setId(componentId);
193 map.put(componentId, availability);
194 }
195 }
196 return map;
197 }
198
199 protected InheritanceConfiguration resolveInheritanceConfiguration(HandlerMetaData area) {
200 Inherits inherits = area.getHandlerClass().getAnnotation(Inherits.class);
201 ConfiguredInheritance inheritance = new ConfiguredInheritance();
202 if (inherits != null) {
203 inheritance.setEnabled(true);
204 inheritance.setComponents(inherits.components().getConfigurationFormat());
205 inheritance.setProperties(inherits.properties().getConfigurationFormat());
206 }
207 return inheritance;
208 }
209
210 protected TemplateAvailability resolveTemplateAvailability(HandlerMetaData template) {
211 Method method = resolveTemplateAvailabilityMethod(template);
212 if (method != null) {
213 return new DefaultTemplateAvailability(template.getHandler(), method);
214 }
215 return null;
216 }
217
218 protected Method resolveTemplateAvailabilityMethod(HandlerMetaData template) {
219
220 final Class<?> handlerClass = template.getHandlerClass();
221 final List<Method> matchingMethods = new ArrayList<Method>();
222
223
224
225 Class<?> clazz = handlerClass;
226 while (matchingMethods.isEmpty() && clazz != null) {
227 Method[] methods = clazz.getDeclaredMethods();
228 for (final Method method : methods) {
229
230
231 if (!method.isAnnotationPresent(Available.class)) {
232 continue;
233 }
234
235
236 if (!method.equals(ClassUtils.getMostSpecificMethod(method, handlerClass))) {
237 continue;
238 }
239
240 if (!method.getReturnType().equals(Boolean.TYPE)) {
241 if (logger.isDebugEnabled()) {
242 logger.debug("Method annotated with @Available has wrong return type [" + method.getClass() + "] should be boolean.");
243 }
244 continue;
245 }
246
247 matchingMethods.add(method);
248 }
249 clazz = clazz.getSuperclass();
250 }
251
252 if (matchingMethods.size() == 0) {
253 return null;
254 } else if (matchingMethods.size() == 1) {
255 return matchingMethods.get(0);
256 } else {
257 throw new IllegalStateException("Multiple @Available annotated methods found for handler [" + handlerClass + "]");
258 }
259 }
260
261 protected ParameterResolver getTemplateAvailabilityParameters(final Node node, final TemplateDefinition templateDefinition) {
262 return new ParameterResolver() {
263
264 @Override
265 public Object resolveParameter(Class<?> parameterType) {
266 if (parameterType.equals(Node.class)) {
267 return node;
268 }
269 if (parameterType.equals(Content.class)) {
270 return ContentUtil.asContent(node);
271 }
272 if (parameterType.equals(TemplateDefinition.class)) {
273 return templateDefinition;
274 }
275 return super.resolveParameter(parameterType);
276 }
277 };
278 }
279
280 private class DefaultTemplateAvailability implements TemplateAvailability {
281
282 private final Object handler;
283 private final Method method;
284
285 public DefaultTemplateAvailability(Object handler, Method method) {
286 this.handler = handler;
287 this.method = method;
288 }
289
290 @Override
291 public boolean isAvailable(Node node, TemplateDefinition templateDefinition) {
292 ParameterResolver parameters = getTemplateAvailabilityParameters(node, templateDefinition);
293 return (Boolean) MethodInvocationUtils.invoke(method, handler, parameters);
294 }
295 }
296 }