1. Project Clover database Fri Apr 29 2016 13:24:33 CEST
  2. Package info.magnolia.module.blossom.template

File TemplateExporter.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart8.png
42% of files have more coverage

Code metrics

110
215
31
1
613
430
99
0.46
6.94
31
3.19
1.7% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
TemplateExporter 89 215 1.7% 99 81
0.772471977.2%
 

Contributing tests

This file is covered by 4 tests. .

Source view

1    /**
2    * This file Copyright (c) 2010-2016 Magnolia International
3    * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10    * This file is distributed in the hope that it will be
11    * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12    * implied warranty of MERCHANTABILITY or FITNESS FOR A
13    * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14    * Redistribution, except as permitted by whichever of the GPL
15    * or MNA you select, is prohibited.
16    *
17    * 1. For the GPL license (GPL), you can redistribute and/or
18    * modify this file under the terms of the GNU General
19    * Public License, Version 3, as published by the Free Software
20    * Foundation. You should have received a copy of the GNU
21    * General Public License, Version 3 along with this program;
22    * if not, write to the Free Software Foundation, Inc., 51
23    * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24    *
25    * 2. For the Magnolia Network Agreement (MNA), this file
26    * and the accompanying materials are made available under the
27    * terms of the MNA which accompanies this distribution, and
28    * is available at http://www.magnolia-cms.com/mna.html
29    *
30    * Any modifications to this file must keep this entire header
31    * intact.
32    *
33    */
34    package info.magnolia.module.blossom.template;
35   
36    import java.lang.annotation.Annotation;
37    import java.lang.reflect.Method;
38    import java.lang.reflect.Modifier;
39    import java.util.ArrayList;
40    import java.util.Collection;
41    import java.util.Collections;
42    import java.util.HashSet;
43    import java.util.List;
44    import java.util.Map;
45    import java.util.Set;
46    import java.util.concurrent.atomic.AtomicReference;
47   
48    import org.apache.commons.lang.StringUtils;
49    import org.slf4j.Logger;
50    import org.slf4j.LoggerFactory;
51    import org.springframework.aop.support.AopUtils;
52    import org.springframework.beans.BeansException;
53    import org.springframework.beans.factory.InitializingBean;
54    import org.springframework.beans.factory.config.BeanPostProcessor;
55    import org.springframework.context.ApplicationEvent;
56    import org.springframework.context.ApplicationListener;
57    import org.springframework.util.ClassUtils;
58    import org.springframework.util.ReflectionUtils;
59    import org.springframework.web.method.HandlerMethod;
60    import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
61    import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
62    import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
63   
64    import info.magnolia.config.registry.DefinitionProvider;
65    import info.magnolia.module.blossom.annotation.Area;
66    import info.magnolia.module.blossom.annotation.ComponentCategory;
67    import info.magnolia.module.blossom.annotation.PreRegister;
68    import info.magnolia.module.blossom.annotation.Template;
69    import info.magnolia.module.blossom.dialog.BlossomDialogDefinitionProvider;
70    import info.magnolia.module.blossom.dialog.BlossomDialogDescription;
71    import info.magnolia.module.blossom.dialog.DialogDescriptionBuilder;
72    import info.magnolia.module.blossom.dispatcher.BlossomDispatcher;
73    import info.magnolia.module.blossom.dispatcher.BlossomDispatcherAware;
74    import info.magnolia.module.blossom.dispatcher.BlossomDispatcherInitializedEvent;
75    import info.magnolia.module.blossom.support.MethodInvocationUtils;
76    import info.magnolia.module.blossom.support.ParameterResolver;
77    import info.magnolia.objectfactory.Components;
78    import info.magnolia.rendering.template.AreaDefinition;
79    import info.magnolia.rendering.template.TemplateDefinition;
80    import info.magnolia.rendering.template.registry.TemplateDefinitionRegistry;
81    import info.magnolia.ui.dialog.definition.DialogDefinition;
82    import info.magnolia.ui.dialog.registry.DialogDefinitionRegistry;
83   
84    /**
85    * Detects templates by inspecting HandlerMappings.
86    *
87    * @since 1.1.1
88    */
 
89    public class TemplateExporter implements BeanPostProcessor, InitializingBean, ApplicationListener, BlossomDispatcherAware {
90   
91    private static final String TEMPLATE_DIALOG_PREFIX = "blossom-template-dialog:";
92    private static final String AREA_DIALOG_PREFIX = "blossom-area-dialog:";
93   
94    private final Logger logger = LoggerFactory.getLogger(getClass());
95   
96    private BlossomDispatcher dispatcher;
97    private TemplateDefinitionBuilder templateDefinitionBuilder;
98    private DialogDescriptionBuilder dialogDescriptionBuilder;
99   
100    private DetectedHandlersMetaData detectedHandlers = new DetectedHandlersMetaData();
101   
 
102    toggle public void setTemplateDefinitionBuilder(TemplateDefinitionBuilder templateDefinitionBuilder) {
103    this.templateDefinitionBuilder = templateDefinitionBuilder;
104    }
105   
 
106    toggle public void setDialogDescriptionBuilder(DialogDescriptionBuilder dialogDescriptionBuilder) {
107    this.dialogDescriptionBuilder = dialogDescriptionBuilder;
108    }
109   
 
110    toggle @Override
111    public void setBlossomDispatcher(BlossomDispatcher dispatcher) {
112    this.dispatcher = dispatcher;
113    }
114   
 
115  60 toggle @Override
116    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
117  60 return bean;
118    }
119   
 
120  64 toggle @Override
121    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
122  64 if (bean instanceof AbstractUrlHandlerMapping) {
123  10 scanHandlerMapping((AbstractUrlHandlerMapping) bean);
124    }
125  64 if (bean instanceof RequestMappingInfoHandlerMapping) {
126  0 scanHandlerMapping((RequestMappingInfoHandlerMapping) bean);
127    }
128  64 return bean;
129    }
130   
 
131  10 toggle protected void scanHandlerMapping(AbstractUrlHandlerMapping handlerMapping) {
132  10 for (Object object : handlerMapping.getHandlerMap().entrySet()) {
133   
134  13 Map.Entry entry = (Map.Entry) object;
135  13 String handlerPath = (String) entry.getKey();
136  13 Object handler = entry.getValue();
137   
138  13 postProcessHandler(handler, handlerPath);
139    }
140    }
141   
 
142  0 toggle private void scanHandlerMapping(RequestMappingInfoHandlerMapping handlerMapping) {
143   
144  0 Map<RequestMappingInfo,HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
145  0 for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
146   
147  0 RequestMappingInfo requestMappingInfo = entry.getKey();
148  0 HandlerMethod handlerMethod = entry.getValue();
149   
150  0 Set<String> patterns = requestMappingInfo.getPatternsCondition().getPatterns();
151  0 if (!patterns.isEmpty()) {
152  0 String pattern = patterns.iterator().next();
153  0 postProcessHandler(handlerMethod.createWithResolvedBean().getBean(), pattern);
154    }
155    }
156    }
157   
 
158  13 toggle protected void postProcessHandler(Object handler, String handlerPath) {
159  13 Class<?> handlerClass = AopUtils.getTargetClass(handler);
160  13 if (handlerClass.isAnnotationPresent(Area.class)) {
161  3 detectedHandlers.addArea(new HandlerMetaData(handler, handlerPath, handlerClass));
162  10 } else if (handlerClass.isAnnotationPresent(Template.class)) {
163  10 detectedHandlers.addTemplate(new HandlerMetaData(handler, handlerPath, handlerClass));
164  10 String templateId = resolveTemplateId(handlerClass);
165  10 if (isComponent(templateId)) {
166  1 List<Class<? extends Annotation>> categories = findAllAnnotationsMetaAnnotatedWith(handlerClass, ComponentCategory.class);
167  1 detectedHandlers.addCategories(templateId, categories);
168    }
169    }
170    }
171   
 
172  13 toggle @Override
173    public void onApplicationEvent(ApplicationEvent event) {
174  13 if (event instanceof BlossomDispatcherInitializedEvent && event.getSource() == dispatcher) {
175  7 exportTemplates();
176    }
177    }
178   
 
179  7 toggle protected void exportTemplates() {
180   
181  7 Set<String> registeredIds = new HashSet<>();
182  7 for (HandlerMetaData template : detectedHandlers.getTemplates()) {
183   
184  10 BlossomTemplateDefinition definition = templateDefinitionBuilder.buildTemplateDefinition(dispatcher, detectedHandlers, template);
185   
186    // Avoid registering the same template again, happens when a controller has more than one @RequestMapping
187  10 if (registeredIds.contains(definition.getId())) {
188  0 continue;
189    }
190  10 registeredIds.add(definition.getId());
191   
192  10 definition = postProcessTemplateDefinition(definition);
193  10 if (definition == null) {
194  1 continue;
195    }
196   
197  9 Components.getComponent(TemplateDefinitionRegistry.class).register(createTemplateDefinitionProvider(definition));
198   
199  9 if (logger.isDebugEnabled()) {
200  0 logger.debug("Registered template [" + template.getHandlerClass() + "] with id [" + definition.getId() + "]");
201    }
202   
203  9 template.setTemplateDefinition(definition);
204   
205  9 if (StringUtils.isEmpty(definition.getDialog())) {
206  9 registerTemplateDialog(definition);
207    }
208   
209  9 registerDialogFactories(definition);
210   
211  9 registerAreaDialogs(definition.getAreas().values());
212    }
213   
214  7 if (logger.isInfoEnabled()) {
215  7 StringBuilder sb = new StringBuilder();
216  7 for (HandlerMetaData template : detectedHandlers.getTemplates()) {
217  10 if (template.getTemplateDefinition() != null) {
218  9 if (sb.length() != 0) {
219  3 sb.append(",");
220    }
221  9 sb.append(template.getTemplateDefinition().getId());
222    }
223    }
224  7 logger.info("Registered templates [" + sb.toString() + "]");
225    }
226   
227    // We're done with this structure so there's no need to keep it around
228  7 detectedHandlers = null;
229    }
230   
 
231  9 toggle protected void registerDialogFactories(BlossomTemplateDefinition templateDefinition) {
232   
233  9 List<BlossomDialogDescription> dialogDescriptions = dialogDescriptionBuilder.buildDescriptions(templateDefinition.getHandler());
234   
235  9 for (BlossomDialogDescription dialogDescription : dialogDescriptions) {
236   
237  2 dialogDescription = postProcessDialogFactoryDialog(templateDefinition, dialogDescription);
238  2 if (dialogDescription == null) {
239  0 continue;
240    }
241   
242  2 Components.getComponent(DialogDefinitionRegistry.class).register(createDialogDefinitionProvider(dialogDescription));
243   
244  2 if (logger.isDebugEnabled()) {
245  0 logger.debug("Registered dialog factory within template [" + templateDefinition.getId() + "] with id [" + dialogDescription.getId() + "]");
246    }
247    }
248    }
249   
 
250  9 toggle protected void registerTemplateDialog(BlossomTemplateDefinition templateDefinition) {
251   
252  9 String templateId = templateDefinition.getId();
253   
254  9 String dialogId = TEMPLATE_DIALOG_PREFIX + AopUtils.getTargetClass(templateDefinition.getHandler()).getName();
255   
256  9 BlossomDialogDescription dialogDescription = dialogDescriptionBuilder.buildDescription(dialogId, templateDefinition.getTitle(), templateDefinition.getHandler());
257   
258  9 if (dialogDescription.getFactoryMetaData().isEmpty()) {
259  6 return;
260    }
261   
262  3 dialogDescription = postProcessTemplateDialog(templateDefinition, dialogDescription);
263  3 if (dialogDescription == null) {
264  0 return;
265    }
266   
267  3 Components.getComponent(DialogDefinitionRegistry.class).register(createDialogDefinitionProvider(dialogDescription));
268   
269  3 templateDefinition.setDialog(dialogId);
270   
271  3 if (logger.isDebugEnabled()) {
272  0 logger.debug("Registered dialog for template [" + templateId + "] with id [" + dialogId + "]");
273    }
274    }
275   
 
276  12 toggle protected void registerAreaDialogs(Collection<AreaDefinition> areas) {
277  12 for (AreaDefinition areaDefinition : areas) {
278  3 if (StringUtils.isEmpty(areaDefinition.getDialog())) {
279  3 registerAreaDialog((BlossomAreaDefinition) areaDefinition);
280    }
281  3 registerAreaDialogs(areaDefinition.getAreas().values());
282    }
283    }
284   
 
285  3 toggle protected void registerAreaDialog(BlossomAreaDefinition areaDefinition) {
286   
287  3 String areaName = areaDefinition.getName();
288   
289  3 String dialogId = AREA_DIALOG_PREFIX + AopUtils.getTargetClass(areaDefinition.getHandler()).getName();
290   
291  3 BlossomDialogDescription dialogDescription = dialogDescriptionBuilder.buildDescription(dialogId, areaDefinition.getTitle(), areaDefinition.getHandler());
292   
293  3 if (dialogDescription.getFactoryMetaData().isEmpty()) {
294  2 return;
295    }
296   
297  1 dialogDescription = postProcessAreaDialog(areaDefinition, dialogDescription);
298  1 if (dialogDescription == null) {
299  0 return;
300    }
301   
302  1 Components.getComponent(DialogDefinitionRegistry.class).register(createDialogDefinitionProvider(dialogDescription));
303   
304  1 areaDefinition.setDialog(dialogId);
305   
306  1 if (logger.isDebugEnabled()) {
307  0 logger.debug("Registered dialog for area [" + areaName + "] with id [" + dialogId + "]");
308    }
309    }
310   
 
311  7 toggle @Override
312    public void afterPropertiesSet() throws Exception {
313  7 if (templateDefinitionBuilder == null) {
314  7 templateDefinitionBuilder = new TemplateDefinitionBuilder();
315    }
316  7 if (dialogDescriptionBuilder == null) {
317  7 dialogDescriptionBuilder = new DialogDescriptionBuilder();
318    }
319    }
320   
 
321  9 toggle protected DefinitionProvider<TemplateDefinition> createTemplateDefinitionProvider(BlossomTemplateDefinition definition) {
322  9 return new BlossomTemplateDefinitionProvider(definition);
323    }
324   
 
325  6 toggle protected DefinitionProvider<DialogDefinition> createDialogDefinitionProvider(BlossomDialogDescription dialogDescription) {
326  6 return new BlossomDialogDefinitionProvider(dialogDescription);
327    }
328   
329    /**
330    * Allows sub-classes to do post-processing of the template definition before it is registered. If it returns null
331    * the template will not be registered and neither will its dialog or any &#64;DialogFactory methods within it.
332    */
 
333  10 toggle protected BlossomTemplateDefinition postProcessTemplateDefinition(final BlossomTemplateDefinition templateDefinition) {
334   
335  10 templateDefinition.setAreas(postProcessAreaDefinitions(templateDefinition, templateDefinition.getAreas()));
336   
337  10 Object handler = templateDefinition.getHandler();
338   
339  10 final Class<?> factoryClass = AopUtils.getTargetClass(handler);
340   
341  10 final List<Method> matchingMethods = new ArrayList<Method>();
342   
343  10 ReflectionUtils.doWithMethods(factoryClass, new ReflectionUtils.MethodCallback() {
344   
 
345  144 toggle @Override
346    public void doWith(Method method) {
347  144 if (method.isAnnotationPresent(PreRegister.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
348  3 if (Modifier.isStatic(method.getModifiers())) {
349  0 throw new IllegalStateException("PreRegister annotation is not supported on static methods");
350    }
351  3 if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(BlossomTemplateDefinition.class)) {
352  2 matchingMethods.add(method);
353    }
354    }
355    }
356    });
357  10 Collections.reverse(matchingMethods);
358   
359  10 final AtomicReference<BlossomTemplateDefinition> reference = new AtomicReference<BlossomTemplateDefinition>(templateDefinition);
360  10 for (Method matchingMethod : matchingMethods) {
361  2 Object returnValue = MethodInvocationUtils.invoke(matchingMethod, handler, new ParameterResolver() {
 
362  2 toggle @Override
363    public Object resolveParameter(Class<?> parameterType) {
364  2 if (parameterType.isAssignableFrom(BlossomTemplateDefinition.class)) {
365  2 return reference.get();
366    }
367  0 return super.resolveParameter(parameterType);
368    }
369    });
370  2 if (returnValue == null && !matchingMethod.getReturnType().equals(Void.TYPE)) {
371  1 return null;
372    }
373  1 if (returnValue instanceof BlossomTemplateDefinition) {
374  0 reference.set((BlossomTemplateDefinition) returnValue);
375    }
376    }
377   
378  9 return reference.get();
379    }
380   
381    /**
382    * Recursively post-process area definitions starting in bottom up order.
383    */
 
384  13 toggle protected Map<String, AreaDefinition> postProcessAreaDefinitions(BlossomTemplateDefinition templateDefinition, Map<String, AreaDefinition> areas) {
385   
386  13 for (String areaName : areas.keySet()) {
387   
388  3 BlossomAreaDefinition areaDefinition = (BlossomAreaDefinition) areas.get(areaName);
389  3 areaDefinition.setAreas(postProcessAreaDefinitions(templateDefinition, areaDefinition.getAreas()));
390   
391  3 areaDefinition = postProcessAreaDefinition(templateDefinition, areaDefinition);
392  3 if (areaDefinition == null) {
393  0 areas.remove(areaName);
394    } else {
395  3 areas.put(areaName, areaDefinition);
396    }
397    }
398   
399  13 return areas;
400    }
401   
 
402  3 toggle protected BlossomAreaDefinition postProcessAreaDefinition(final BlossomTemplateDefinition templateDefinition, BlossomAreaDefinition areaDefinition) {
403   
404  3 Object handler = areaDefinition.getHandler();
405   
406  3 final Class<?> factoryClass = AopUtils.getTargetClass(handler);
407   
408  3 final List<Method> matchingMethods = new ArrayList<Method>();
409   
410  3 ReflectionUtils.doWithMethods(factoryClass, new ReflectionUtils.MethodCallback() {
411   
 
412  44 toggle @Override
413    public void doWith(Method method) {
414  44 if (method.isAnnotationPresent(PreRegister.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
415  4 if (Modifier.isStatic(method.getModifiers())) {
416  0 throw new IllegalStateException("PreRegister annotation is not supported on static methods");
417    }
418  4 if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(BlossomAreaDefinition.class)) {
419  2 matchingMethods.add(method);
420    }
421    }
422    }
423    });
424  3 Collections.reverse(matchingMethods);
425   
426  3 final AtomicReference<BlossomAreaDefinition> reference = new AtomicReference<BlossomAreaDefinition>(areaDefinition);
427  3 for (Method matchingMethod : matchingMethods) {
428  2 Object returnValue = MethodInvocationUtils.invoke(matchingMethod, handler, new ParameterResolver() {
 
429  2 toggle @Override
430    public Object resolveParameter(Class<?> parameterType) {
431  2 if (parameterType.isAssignableFrom(BlossomAreaDefinition.class)) {
432  2 return reference.get();
433    }
434  0 if (parameterType.isAssignableFrom(BlossomTemplateDefinition.class)) {
435  0 return templateDefinition;
436    }
437  0 return super.resolveParameter(parameterType);
438    }
439    });
440  2 if (returnValue == null && !matchingMethod.getReturnType().equals(Void.TYPE)) {
441  0 return null;
442    }
443  2 if (returnValue instanceof BlossomAreaDefinition) {
444  0 reference.set((BlossomAreaDefinition) returnValue);
445    }
446    }
447   
448  3 return reference.get();
449    }
450   
451    /**
452    * Allows sub-classes to do post-processing on dialogs for &#64;DialogFactory annotated methods before they're
453    * registered. If it returns null the dialog will not be registered.
454    */
 
455  2 toggle protected BlossomDialogDescription postProcessDialogFactoryDialog(BlossomTemplateDefinition templateDefinition, BlossomDialogDescription dialogDescription) {
456  2 return dialogDescription;
457    }
458   
459    /**
460    * Allows sub-classes to do post-processing on dialogs for templates before they're registered. If it returns null
461    * the dialog will not be registered and the template will not have a dialog.
462    */
 
463  3 toggle protected BlossomDialogDescription postProcessTemplateDialog(final BlossomTemplateDefinition templateDefinition, final BlossomDialogDescription dialogDescription) {
464  3 Object factoryObject = dialogDescription.getFactoryMetaData().getFactoryObject();
465   
466  3 final Class<?> factoryClass = AopUtils.getTargetClass(factoryObject);
467   
468  3 final List<Method> matchingMethods = new ArrayList<Method>();
469   
470  3 ReflectionUtils.doWithMethods(factoryClass, new ReflectionUtils.MethodCallback() {
471   
 
472  46 toggle @Override
473    public void doWith(Method method) {
474  46 if (method.isAnnotationPresent(PreRegister.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
475  2 if (Modifier.isStatic(method.getModifiers())) {
476  0 throw new IllegalStateException("PreRegister annotation is not supported on static methods");
477    }
478  2 if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(BlossomDialogDescription.class)) {
479  1 matchingMethods.add(method);
480    }
481    }
482    }
483    });
484  3 Collections.reverse(matchingMethods);
485   
486  3 final AtomicReference<BlossomDialogDescription> reference = new AtomicReference<BlossomDialogDescription>(dialogDescription);
487  3 for (Method matchingMethod : matchingMethods) {
488  1 Object returnValue = MethodInvocationUtils.invoke(matchingMethod, factoryObject, new ParameterResolver() {
 
489  1 toggle @Override
490    public Object resolveParameter(Class<?> parameterType) {
491  1 if (parameterType.isAssignableFrom(BlossomDialogDescription.class)) {
492  1 return reference.get();
493    }
494  0 if (parameterType.isAssignableFrom(BlossomTemplateDefinition.class)) {
495  0 return templateDefinition;
496    }
497  0 return super.resolveParameter(parameterType);
498    }
499    });
500  1 if (returnValue == null && !matchingMethod.getReturnType().equals(Void.TYPE)) {
501  0 return null;
502    }
503  1 if (returnValue instanceof BlossomDialogDescription) {
504  0 reference.set((BlossomDialogDescription) returnValue);
505    }
506    }
507   
508  3 return reference.get();
509    }
510   
511    /**
512    * Allows sub-classes to do post-processing on dialogs for areas before they're registered. If it returns null the
513    * dialog will not be registered and the area will not have a dialog.
514    */
 
515  1 toggle protected BlossomDialogDescription postProcessAreaDialog(final BlossomAreaDefinition areaDefinition, final BlossomDialogDescription dialogDescription) {
516   
517  1 Object factoryObject = dialogDescription.getFactoryMetaData().getFactoryObject();
518   
519  1 final Class<?> factoryClass = AopUtils.getTargetClass(factoryObject);
520   
521  1 final List<Method> matchingMethods = new ArrayList<Method>();
522   
523  1 ReflectionUtils.doWithMethods(factoryClass, new ReflectionUtils.MethodCallback() {
524   
 
525  18 toggle @Override
526    public void doWith(Method method) {
527  18 if (method.isAnnotationPresent(PreRegister.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
528  4 if (Modifier.isStatic(method.getModifiers())) {
529  0 throw new IllegalStateException("PreRegister annotation is not supported on static methods");
530    }
531  4 if (method.getParameterTypes().length >= 1 && method.getParameterTypes()[0].isAssignableFrom(BlossomDialogDescription.class)) {
532  2 matchingMethods.add(method);
533    }
534    }
535    }
536    });
537  1 Collections.reverse(matchingMethods);
538   
539  1 final AtomicReference<BlossomDialogDescription> reference = new AtomicReference<BlossomDialogDescription>(dialogDescription);
540  1 for (Method matchingMethod : matchingMethods) {
541  2 Object returnValue = MethodInvocationUtils.invoke(matchingMethod, factoryObject, new ParameterResolver() {
 
542  2 toggle @Override
543    public Object resolveParameter(Class<?> parameterType) {
544  2 if (parameterType.isAssignableFrom(BlossomDialogDescription.class)) {
545  2 return reference.get();
546    }
547  0 if (parameterType.isAssignableFrom(BlossomAreaDefinition.class)) {
548  0 return areaDefinition;
549    }
550  0 return super.resolveParameter(parameterType);
551    }
552    });
553  2 if (returnValue == null && !matchingMethod.getReturnType().equals(Void.TYPE)) {
554  0 return null;
555    }
556  2 if (returnValue instanceof BlossomDialogDescription) {
557  0 reference.set((BlossomDialogDescription) returnValue);
558    }
559    }
560   
561  1 return reference.get();
562    }
563   
564    /**
565    * Resolves the template id for a handler class as specified in the &#64;Template annotation.
566    *
567    * @param handlerClass handler class to find the template id for
568    * @return the template id specified
569    * @throws IllegalArgumentException if the handler class is not annotated with @Template
570    */
 
571  10 toggle private String resolveTemplateId(Class<?> handlerClass) {
572  10 Template annotation = handlerClass.getAnnotation(Template.class);
573  10 if (annotation == null) {
574  0 throw new IllegalArgumentException("Could not resolve template id, @Template is not present on class [" + handlerClass.getName() + "]");
575    }
576  10 return annotation.id();
577    }
578   
579    /**
580    * Returns all annotations on a class that have themselves been annotated with a certain annotation, i.e. meta annotated.
581    *
582    * @param clazz the class to introspect
583    * @param targetAnnotationType the annotation to look for
584    * @return a list of the found annotations, never null
585    */
 
586  1 toggle private List<Class<? extends Annotation>> findAllAnnotationsMetaAnnotatedWith(Class<?> clazz, Class<? extends Annotation> targetAnnotationType) {
587  1 List<Class<? extends Annotation>> annotationsWithMetaAnnotations = new ArrayList<Class<? extends Annotation>>();
588  1 Annotation[] annotations = clazz.getAnnotations();
589  1 for (Annotation annotation : annotations) {
590  3 Class<? extends Annotation> annotationType = annotation.annotationType();
591  3 Annotation[] metaAnnotations = annotationType.getAnnotations();
592  3 for (Annotation metaAnnotation : metaAnnotations) {
593  9 Class<? extends Annotation> metaAnnotationType = metaAnnotation.annotationType();
594  9 if (metaAnnotationType.equals(targetAnnotationType)) {
595  1 annotationsWithMetaAnnotations.add(annotationType);
596    }
597    }
598    }
599  1 return annotationsWithMetaAnnotations;
600    }
601   
602    /**
603    * Tests if a template id is a component.
604    *
605    * @param templateId the template id to evaluate
606    * @return true if the template id is a component
607    */
 
608  10 toggle private boolean isComponent(String templateId) {
609  10 String path = StringUtils.substringAfter(templateId, ":");
610  10 path = StringUtils.removeStart(path, "/");
611  10 return path.startsWith("components/");
612    }
613    }