View Javadoc

1   /**
2    * This file Copyright (c) 2003-2011 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.dialog;
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.List;
41  
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  import org.springframework.aop.support.AopUtils;
45  import org.springframework.util.ClassUtils;
46  import org.springframework.util.ReflectionUtils;
47  
48  import info.magnolia.module.admininterface.SaveHandler;
49  import info.magnolia.module.blossom.annotation.DialogFactory;
50  import info.magnolia.module.blossom.annotation.DialogValidator;
51  import info.magnolia.module.blossom.annotation.I18nBasename;
52  import info.magnolia.module.blossom.annotation.InitSaveHandler;
53  import info.magnolia.module.blossom.annotation.TabFactory;
54  import info.magnolia.module.blossom.annotation.TabOrder;
55  import info.magnolia.module.blossom.annotation.TabValidator;
56  
57  /**
58   * Builds dialog descriptions from annotations.
59   *
60   * @since 1.0
61   */
62  public class DialogDescriptionBuilder {
63  
64      private final Logger logger = LoggerFactory.getLogger(getClass());
65  
66      private DialogCreator dialogCreator;
67  
68      public void setDialogCreator(DialogCreator dialogCreator) {
69          this.dialogCreator = dialogCreator;
70      }
71  
72      public DialogCreator getDialogCreator() {
73          if (dialogCreator == null) {
74              dialogCreator = new DefaultDialogCreator();
75          }
76          return dialogCreator;
77      }
78  
79      public BlossomDialogDescription buildDescription(Object factoryObject) {
80          return buildDescription(factoryObject, null);
81      }
82  
83      public List<BlossomDialogDescription> buildDescriptions(final Object handler) {
84  
85          final List<Method> factoryMethods = new ArrayList<Method>();
86  
87          final Class<?> handlerClass = AopUtils.getTargetClass(handler);
88          ReflectionUtils.doWithMethods(handlerClass, new ReflectionUtils.MethodCallback() {
89  
90              @Override
91              public void doWith(Method method) {
92                  DialogFactory dialogFactory = method.getAnnotation(DialogFactory.class);
93                  if (dialogFactory != null && method.equals(ClassUtils.getMostSpecificMethod(method, handlerClass))) {
94                      if (Modifier.isStatic(method.getModifiers())) {
95                          throw new IllegalStateException("DialogFactory annotation is not supported on static methods");
96                      }
97                      factoryMethods.add(method);
98                  }
99              }
100         });
101 
102         List<BlossomDialogDescription> descriptions = new ArrayList<BlossomDialogDescription>();
103 
104         for (Method method : factoryMethods) {
105             descriptions.add(buildDescription(handler, method));
106         }
107 
108         return descriptions;
109     }
110 
111     public BlossomDialogDescription buildDescription(Object factoryObject, Method factoryMethod) {
112         DialogFactory annotation = findAnnotation(DialogFactory.class, factoryObject, factoryMethod);
113         return buildDescription(annotation.value(), annotation.label(), factoryObject, factoryMethod);
114     }
115 
116     public BlossomDialogDescription buildDescription(String name, String label, Object factoryObject) {
117         return buildDescription(name, label, factoryObject, null);
118     }
119 
120     protected BlossomDialogDescription buildDescription(String id, String label, Object factoryObject, Method factoryMethod) {
121         TabOrder tabOrder = findAnnotation(TabOrder.class, factoryObject, factoryMethod);
122         I18nBasename i18nBasename = findAnnotation(I18nBasename.class, factoryObject, factoryMethod);
123         final DialogFactoryMetaData factoryMetaData = new DialogFactoryMetaData();
124         factoryMetaData.setLabel(label);
125         factoryMetaData.setI18nBasename(i18nBasename != null ? i18nBasename.value() : null);
126         factoryMetaData.setFactoryObject(factoryObject);
127         factoryMetaData.setFactoryMethod(factoryMethod);
128         factoryMetaData.setTabOrder(tabOrder != null ? tabOrder.value() : null);
129         factoryMetaData.setInitSaveHandlerMethod(findInitSaveHandlerMethod(factoryObject));
130 
131         final Class<?> factoryClass = AopUtils.getTargetClass(factoryObject);
132 
133         ReflectionUtils.doWithMethods(factoryClass, new ReflectionUtils.MethodCallback() {
134 
135             @Override
136             public void doWith(Method method) {
137                 if (method.isAnnotationPresent(TabFactory.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
138                     if (Modifier.isStatic(method.getModifiers())) {
139                         throw new IllegalStateException("TabFactory annotation is not supported on static methods");
140                     }
141                     factoryMetaData.addTabFactory(method);
142                 }
143                 if (method.isAnnotationPresent(TabValidator.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
144                     if (Modifier.isStatic(method.getModifiers())) {
145                         throw new IllegalStateException("TabValidator annotation is not supported on static methods");
146                     }
147                     factoryMetaData.addTabValidator(method);
148                 }
149                 if (method.isAnnotationPresent(DialogValidator.class) && method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
150                     if (Modifier.isStatic(method.getModifiers())) {
151                         throw new IllegalStateException("DialogValidator annotation is not supported on static methods");
152                     }
153                     factoryMetaData.addDialogValidator(method);
154                 }
155             }
156         });
157 
158         BlossomDialogDescription dialogDescription = new BlossomDialogDescription();
159         dialogDescription.setId(id);
160         dialogDescription.setFactoryMetaData(factoryMetaData);
161         dialogDescription.setDialogClass(BlossomConfiguredDialog.class.getName());
162         dialogDescription.setDialogCreator(getDialogCreator());
163 
164         return dialogDescription;
165     }
166 
167     protected <T extends Annotation> T findAnnotation(Class<T> annotationClass, Object factoryObject, Method factoryMethod) {
168         if (factoryMethod != null) {
169             return factoryMethod.getAnnotation(annotationClass);
170         }
171         Class<?> factoryClass = AopUtils.getTargetClass(factoryObject);
172         return factoryClass.getAnnotation(annotationClass);
173     }
174 
175     protected Method findInitSaveHandlerMethod(final Object factoryObject) {
176 
177         final Class<?> factoryClass = AopUtils.getTargetClass(factoryObject);
178 
179         final List<Method> matchingMethods = new ArrayList<Method>();
180 
181         // Iterate class hierarchy to find the method also in super classes
182 
183         Class<?> clazz = factoryObject.getClass();
184         while (matchingMethods.isEmpty() && clazz != null) {
185             Method[] methods = clazz.getDeclaredMethods();
186             for (final Method method : methods) {
187 
188                 // The method must have the annotation
189                 if (!method.isAnnotationPresent(InitSaveHandler.class)) {
190                     continue;
191                 }
192 
193                 // The method must not be overridden
194                 if (!method.equals(ClassUtils.getMostSpecificMethod(method, factoryClass))) {
195                     continue;
196                 }
197 
198                 if (!method.getReturnType().equals(SaveHandler.class)) {
199                     if (logger.isDebugEnabled()) {
200                         logger.debug("Method annotated with @InitSaveHandler has wrong return type [" + method.getClass() + "] should be SaveHandler.");
201                     }
202                     continue;
203                 }
204 
205                 matchingMethods.add(method);
206             }
207             clazz = clazz.getSuperclass();
208         }
209 
210         if (!matchingMethods.isEmpty()) {
211             if (matchingMethods.size() == 1) {
212                 return matchingMethods.get(0);
213             } else {
214                 logger.error("Multiple @InitSaveHandler annotated methods found in [" + factoryObject.getClass() + "]");
215             }
216         }
217 
218         return null;
219     }
220 }