1. Project Clover database Fri Mar 6 2015 14:07:48 CET
  2. Package info.magnolia.module.blossom.template

File TemplateExporter.java

 

Coverage histogram

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

Code metrics

108
211
31
1
604
423
98
0.46
6.81
31
3.16
1.7% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
TemplateExporter 87 211 1.7% 98 79
0.7742857377.4%
 

Contributing tests

This file is covered by 4 tests. .

Source view

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